Loading libraries

library(GEOquery)
Loading required package: Biobase
Loading required package: BiocGenerics

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename,
    cbind, colnames, dirname, do.call, duplicated, eval,
    evalq, Filter, Find, get, grep, grepl, intersect,
    is.unsorted, lapply, Map, mapply, match, mget,
    order, paste, pmax, pmax.int, pmin, pmin.int,
    Position, rank, rbind, Reduce, rownames, sapply,
    setdiff, sort, table, tapply, union, unique,
    unsplit, which.max, which.min

Welcome to Bioconductor

    Vignettes contain introductory material; view with
    'browseVignettes()'. To cite Bioconductor, see
    'citation("Biobase")', and for packages
    'citation("pkgname")'.
library(oligo)
Loading required package: oligoClasses
Welcome to oligoClasses version 1.56.0
Loading required package: Biostrings
Loading required package: S4Vectors
Loading required package: stats4

Attaching package: ‘S4Vectors’

The following objects are masked from ‘package:base’:

    expand.grid, I, unname

Loading required package: IRanges
Loading required package: XVector
Loading required package: GenomeInfoDb

Attaching package: ‘Biostrings’

The following object is masked from ‘package:base’:

    strsplit

================================================================
Welcome to oligo version 1.58.0
================================================================
library(sva)
Loading required package: mgcv
Loading required package: nlme

Attaching package: ‘nlme’

The following object is masked from ‘package:Biostrings’:

    collapse

The following object is masked from ‘package:IRanges’:

    collapse

This is mgcv 1.8-38. For overview type 'help("mgcv-package")'.
Loading required package: genefilter
Loading required package: BiocParallel
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.6     ✓ dplyr   1.0.7
✓ tidyr   1.1.4     ✓ stringr 1.4.0
✓ readr   2.1.0     ✓ forcats 0.5.1
── Conflicts ────────────────────────── tidyverse_conflicts() ──
x dplyr::collapse()   masks nlme::collapse(), Biostrings::collapse(), IRanges::collapse()
x dplyr::combine()    masks Biobase::combine(), BiocGenerics::combine()
x purrr::compact()    masks XVector::compact()
x dplyr::desc()       masks IRanges::desc()
x tidyr::expand()     masks S4Vectors::expand()
x dplyr::filter()     masks stats::filter()
x dplyr::first()      masks S4Vectors::first()
x dplyr::lag()        masks stats::lag()
x ggplot2::Position() masks BiocGenerics::Position(), base::Position()
x purrr::reduce()     masks IRanges::reduce()
x dplyr::rename()     masks S4Vectors::rename()
x dplyr::slice()      masks XVector::slice(), IRanges::slice()
x readr::spec()       masks genefilter::spec()
x dplyr::summarize()  masks oligo::summarize()
library(ggsci)
library(factoextra)
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(pheatmap)
library(dendextend)

---------------------
Welcome to dendextend version 1.15.2
Type citation('dendextend') for how to cite the package.

Type browseVignettes(package = 'dendextend') for the package vignette.
The github page is: https://github.com/talgalili/dendextend/

Suggestions and bug-reports can be submitted at: https://github.com/talgalili/dendextend/issues
You may ask questions at stackoverflow, use the r and dendextend tags: 
     https://stackoverflow.com/questions/tagged/dendextend

    To suppress this message use:  suppressPackageStartupMessages(library(dendextend))
---------------------


Attaching package: ‘dendextend’

The following object is masked from ‘package:Biostrings’:

    nnodes

The following object is masked from ‘package:stats’:

    cutree
library(caret)
Loading required package: lattice

Attaching package: ‘caret’

The following object is masked from ‘package:purrr’:

    lift
library(RColorBrewer)
library(viridis)
Loading required package: viridisLite
library(UpSetR)

Attaching package: ‘UpSetR’

The following object is masked from ‘package:lattice’:

    histogram
library(ComplexUpset)

Attaching package: ‘ComplexUpset’

The following object is masked from ‘package:UpSetR’:

    upset

Custom functions

# given a matrix, perform min-max scaling on its columns
min_max_mat <- function(mat){
  mat_rescaled <- apply(mat, 2, function(v){
    v_range <- range(v)
    names(v_range) <- c("minimum", "maximum")
    range_difference <- v_range["maximum"] - v_range["minimum"]
    rescaled <- (v - v_range["minimum"])/range_difference
    return(rescaled)
  })
  return(mat_rescaled)
}

Getting data from GEOquery

# geodata <- GEOquery::getGEO(GEO = "GSE76275", destdir = "./tempfiles")
# geodata <- GEOquery::getGEO(filename = "./tempfiles/GSE76275_series_matrix.txt.gz")
# saveRDS(geodata, "geodata.RDS")
geodata <- readRDS("geodata.RDS")
# mdata <- geodata %>% 
#   pluck(1) %>% 
#   phenoData() %>%
#   pData() %>% as_tibble()
# feature_data <- geodata %>% 
#   pluck(1) %>% 
#   featureData()
  
# write_csv(mdata, "raw_mdata.csv")
mdata <- read_csv("raw_mdata.csv")
Rows: 265 Columns: 69
── Column specification ────────────────────────────────────────
Delimiter: ","
chr (62): title, geo_accession, status, submission_date, las...
dbl  (6): channel_count, taxid_ch1, contact_zip/postal_code,...
lgl  (1): growth_protocol_ch1

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# saveRDS(feature_data, "featureData.RDS")
# feature_data <- readRDS("featureData.RDS")

Inspecting and cleaning the metadata

mdata %>% 
  glimpse()
Rows: 265
Columns: 23
$ title                                <chr> "S1-H10", "S1-H14…
$ submission_date                      <chr> "Dec 17 2015", "D…
$ last_update_date                     <chr> "Dec 18 2015", "D…
$ geo_accession                        <chr> "GSM1974566", "GS…
$ `age (years):ch1`                    <dbl> NA, 41, 55, 55, 6…
$ `ajcc stage (7th edition, 2010):ch1` <chr> "T2N1M0", "T1N0M0…
$ `body mass index:ch1`                <dbl> 32, 29, NA, 31, 3…
$ `er:ch1`                             <chr> "Negative", "Nega…
$ `gender:ch1`                         <chr> "Female", "Female…
$ `her2:ch1`                           <chr> "Negative", "Nega…
$ `histology group:ch1`                <chr> "Infiltrating Duc…
$ `histology:ch1`                      <chr> "Infiltrating Duc…
$ `menopausal status:ch1`              <chr> "Post-Menopausal"…
$ `metastases:ch1`                     <chr> "No mets", "No me…
$ `positive nodes:ch1`                 <chr> "1 - 3", "0", "0"…
$ `pr:ch1`                             <chr> "Negative", "Nega…
$ `race:ch1`                           <chr> "Caucasian", "Cau…
$ `set:ch1`                            <chr> "Validation TN", …
$ `tissue:ch1`                         <chr> "Breast cancer", …
$ `tnbc subtype:ch1`                   <chr> "Mesenchymal (MES…
$ `triple-negative status:ch1`         <chr> "TN", "TN", "TN",…
$ `tumor grade:ch1`                    <chr> NA, "Poorly Diffe…
$ `tumor size:ch1`                     <chr> "2 - 5 cm", "<=2c…
mdata <- mdata %>% 
  select(title, contains("date"), geo_accession, contains(":ch1"))

colnames(mdata)
 [1] "title"                             
 [2] "submission_date"                   
 [3] "last_update_date"                  
 [4] "geo_accession"                     
 [5] "age (years):ch1"                   
 [6] "ajcc stage (7th edition, 2010):ch1"
 [7] "body mass index:ch1"               
 [8] "er:ch1"                            
 [9] "gender:ch1"                        
[10] "her2:ch1"                          
[11] "histology group:ch1"               
[12] "histology:ch1"                     
[13] "menopausal status:ch1"             
[14] "metastases:ch1"                    
[15] "positive nodes:ch1"                
[16] "pr:ch1"                            
[17] "race:ch1"                          
[18] "set:ch1"                           
[19] "tissue:ch1"                        
[20] "tnbc subtype:ch1"                  
[21] "triple-negative status:ch1"        
[22] "tumor grade:ch1"                   
[23] "tumor size:ch1"                    
  
cnames <- colnames(mdata)
cnames_processed <- str_split(cnames, pattern = ":") %>% 
  map_chr(~{.x[[1]]}) %>% 
  str_replace_all(" ", "_") %>% 
  str_replace_all("-", "_") %>% 
  str_remove_all("\\(|\\)|,")

cnames_processed
 [1] "title"                       "submission_date"            
 [3] "last_update_date"            "geo_accession"              
 [5] "age_years"                   "ajcc_stage_7th_edition_2010"
 [7] "body_mass_index"             "er"                         
 [9] "gender"                      "her2"                       
[11] "histology_group"             "histology"                  
[13] "menopausal_status"           "metastases"                 
[15] "positive_nodes"              "pr"                         
[17] "race"                        "set"                        
[19] "tissue"                      "tnbc_subtype"               
[21] "triple_negative_status"      "tumor_grade"                
[23] "tumor_size"                 
colnames(mdata) <- cnames_processed
rm(cnames, cnames_processed)
glimpse(mdata)
Rows: 265
Columns: 23
$ title                       <chr> "S1-H10", "S1-H14", "S1-H1…
$ submission_date             <chr> "Dec 17 2015", "Dec 17 201…
$ last_update_date            <chr> "Dec 18 2015", "Dec 18 201…
$ geo_accession               <chr> "GSM1974566", "GSM1974567"…
$ age_years                   <dbl> NA, 41, 55, 55, 65, 40, 66…
$ ajcc_stage_7th_edition_2010 <chr> "T2N1M0", "T1N0M0", "T2N0M…
$ body_mass_index             <dbl> 32, 29, NA, 31, 38, 22, 22…
$ er                          <chr> "Negative", "Negative", "N…
$ gender                      <chr> "Female", "Female", "Femal…
$ her2                        <chr> "Negative", "Negative", "N…
$ histology_group             <chr> "Infiltrating Ductal Carci…
$ histology                   <chr> "Infiltrating Ductal Carci…
$ menopausal_status           <chr> "Post-Menopausal", "Post-M…
$ metastases                  <chr> "No mets", "No mets", "No …
$ positive_nodes              <chr> "1 - 3", "0", "0", "0", "4…
$ pr                          <chr> "Negative", "Negative", "N…
$ race                        <chr> "Caucasian", "Caucasian", …
$ set                         <chr> "Validation TN", "Validati…
$ tissue                      <chr> "Breast cancer", "Breast c…
$ tnbc_subtype                <chr> "Mesenchymal (MES)", "Basa…
$ triple_negative_status      <chr> "TN", "TN", "TN", "TN", "T…
$ tumor_grade                 <chr> NA, "Poorly Differentiated…
$ tumor_size                  <chr> "2 - 5 cm", "<=2cm", "2 - …
mdata <- mdata %>% 
  mutate(her2 = if_else(!is.na(her2), her2, "Not Available")) %>% 
  mutate(er = factor(er, levels = c("Negative", "Positive")), 
         pr = factor(pr, levels = c("Negative", "Positive")), 
         her2 = factor(her2, levels = c("Negative", "Positive", "Not Available"))) %>% 
  select(geo_accession, everything())
head(mdata) 

Reading in raw probe intensity data

Celfiles downloaded from GEO and kept the folder celfiles/

celFiles <- list.celfiles('celfiles/', full.names = TRUE, listGzipped = TRUE)
celFiles %>% head()
[1] "celfiles//GSM1974566_S1_H10.CEL.gz" 
[2] "celfiles//GSM1974567_S1_H14.CEL.gz" 
[3] "celfiles//GSM1974568_S1_H19.CEL.gz" 
[4] "celfiles//GSM1974569_S1_H20B.CEL.gz"
[5] "celfiles//GSM1974570_S1_H22.CEL.gz" 
[6] "celfiles//GSM1974571_S1_H27.CEL.gz" 
names(celFiles) <- celFiles %>% 
  basename() %>% 
  str_split("\\.") %>% 
  map_chr(~{.x[1]}) %>% 
  str_split("_") %>% 
  map_chr(~{.x[1]}) 

head(celFiles)
                           GSM1974566 
 "celfiles//GSM1974566_S1_H10.CEL.gz" 
                           GSM1974567 
 "celfiles//GSM1974567_S1_H14.CEL.gz" 
                           GSM1974568 
 "celfiles//GSM1974568_S1_H19.CEL.gz" 
                           GSM1974569 
"celfiles//GSM1974569_S1_H20B.CEL.gz" 
                           GSM1974570 
 "celfiles//GSM1974570_S1_H22.CEL.gz" 
                           GSM1974571 
 "celfiles//GSM1974571_S1_H27.CEL.gz" 

Rearranging rows of metadata to match order of samples in celFiles.

mdata <- mdata[match(mdata$geo_accession, names(celFiles)), ]

Getting only the relevant variables from the metadata.

mdata_subset <- mdata %>%
  select(geo_accession, 
         title, 
         triple_negative_status, 
         tnbc_subtype,
         submission_date,
         er,
         her2,
         pr,
         race,
         set,
         gender, 
         age_years) %>% 
  mutate(across(where(is.character), .fns = factor)) %>% 
  mutate(tnbc_subtype = if_else(is.na(as.character(tnbc_subtype)), "Not Applicable", as.character(tnbc_subtype))) %>% 
  mutate(tnbc_subtype = factor(tnbc_subtype)) %>% 
  as.data.frame()


rownames(mdata_subset) <- as.character(mdata_subset$geo_accession)

head(mdata_subset)
NA
rawData <- read.celfiles(celFiles, phenoData = AnnotatedDataFrame(mdata_subset))
Platform design info loaded.
Reading in : celfiles//GSM1974566_S1_H10.CEL.gz
Reading in : celfiles//GSM1974567_S1_H14.CEL.gz
Reading in : celfiles//GSM1974568_S1_H19.CEL.gz
Reading in : celfiles//GSM1974569_S1_H20B.CEL.gz
Reading in : celfiles//GSM1974570_S1_H22.CEL.gz
Reading in : celfiles//GSM1974571_S1_H27.CEL.gz
Reading in : celfiles//GSM1974572_S1_H28.CEL.gz
Reading in : celfiles//GSM1974573_S1_H29.CEL.gz
Reading in : celfiles//GSM1974574_S1_H2B.CEL.gz
Reading in : celfiles//GSM1974575_S1_H31.CEL.gz
Reading in : celfiles//GSM1974576_S1_H35B.CEL.gz
Reading in : celfiles//GSM1974577_S1_H36.CEL.gz
Reading in : celfiles//GSM1974578_S1_H38.CEL.gz
Reading in : celfiles//GSM1974579_S1_H3B.CEL.gz
Reading in : celfiles//GSM1974580_S1_H40.CEL.gz
Reading in : celfiles//GSM1974581_S1_H41.CEL.gz
Reading in : celfiles//GSM1974582_S2_H43.CEL.gz
Reading in : celfiles//GSM1974583_S2_H44.CEL.gz
Reading in : celfiles//GSM1974584_S2_H45.CEL.gz
Reading in : celfiles//GSM1974585_S2_H46.CEL.gz
Reading in : celfiles//GSM1974586_S2_H47.CEL.gz
Reading in : celfiles//GSM1974587_S2_H48.CEL.gz
Reading in : celfiles//GSM1974588_S2_H49.CEL.gz
Reading in : celfiles//GSM1974589_S1_H4B.CEL.gz
Reading in : celfiles//GSM1974590_S2_H50.CEL.gz
Reading in : celfiles//GSM1974591_S2_H51.CEL.gz
Reading in : celfiles//GSM1974592_S2_H52.CEL.gz
Reading in : celfiles//GSM1974593_S2_H53.CEL.gz
Reading in : celfiles//GSM1974594_S2_H58B.CEL.gz
Reading in : celfiles//GSM1974595_S2_H59.CEL.gz
Reading in : celfiles//GSM1974596_S1_H6.CEL.gz
Reading in : celfiles//GSM1974597_S2_H60.CEL.gz
Reading in : celfiles//GSM1974598_S2_H61.CEL.gz
Reading in : celfiles//GSM1974599_S2_H62.CEL.gz
Reading in : celfiles//GSM1974600_S2_H63.CEL.gz
Reading in : celfiles//GSM1974601_S2_H64.CEL.gz
Reading in : celfiles//GSM1974602_S2_H65.CEL.gz
Reading in : celfiles//GSM1974603_S2_H66.CEL.gz
Reading in : celfiles//GSM1974604_S2_H67.CEL.gz
Reading in : celfiles//GSM1974605_S2_H68.CEL.gz
Reading in : celfiles//GSM1974606_S2_H69.CEL.gz
Reading in : celfiles//GSM1974607_S1_H7.CEL.gz
Reading in : celfiles//GSM1974608_S2_H70.CEL.gz
Reading in : celfiles//GSM1974609_S2_H71.CEL.gz
Reading in : celfiles//GSM1974610_S2_H72B.CEL.gz
Reading in : celfiles//GSM1974611_S2_H73.CEL.gz
Reading in : celfiles//GSM1974612_S1_H8.CEL.gz
Reading in : celfiles//GSM1974613_S1_H9.CEL.gz
Reading in : celfiles//GSM1974614_S2_H54B.CEL.gz
Reading in : celfiles//GSM1974615_S2_H55B.CEL.gz
Reading in : celfiles//GSM1974616_S2_H56B.CEL.gz
Reading in : celfiles//GSM1974617_S2_H57C.CEL.gz
Reading in : celfiles//GSM1974618_S2_H76.CEL.gz
Reading in : celfiles//GSM1974619_S2_H77.CEL.gz
Reading in : celfiles//GSM1974620_S2_H78.CEL.gz
Reading in : celfiles//GSM1974621_S2_H79.CEL.gz
Reading in : celfiles//GSM1974622_S2_H80.CEL.gz
Reading in : celfiles//GSM1974623_S2_H81.CEL.gz
Reading in : celfiles//GSM1974624_S2_H82.CEL.gz
Reading in : celfiles//GSM1974625_S2_H83.CEL.gz
Reading in : celfiles//GSM1974626_S2_H84.CEL.gz
Reading in : celfiles//GSM1974627_S2_H85B.CEL.gz
Reading in : celfiles//GSM1974628_S2_H88.CEL.gz
Reading in : celfiles//GSM1974629_S2_H89.CEL.gz
Reading in : celfiles//GSM1974630_S2_H90.CEL.gz
Reading in : celfiles//GSM1974631_S2_H91B.CEL.gz
Reading in : celfiles//GSM1974632_S3_H100C.CEL.gz
Reading in : celfiles//GSM1974633_S3_H102C.CEL.gz
Reading in : celfiles//GSM1974634_S3_H103C.CEL.gz
Reading in : celfiles//GSM1974635_S3_H104C.CEL.gz
Reading in : celfiles//GSM1974636_S3_H105C.CEL.gz
Reading in : celfiles//GSM1974637_S3_H106C.CEL.gz
Reading in : celfiles//GSM1974638_S3_H107C.CEL.gz
Reading in : celfiles//GSM1974639_S3_H108C.CEL.gz
Reading in : celfiles//GSM1974640_S3_H109C.CEL.gz
Reading in : celfiles//GSM1974641_S3_H110C.CEL.gz
Reading in : celfiles//GSM1974642_S3_H111C.CEL.gz
Reading in : celfiles//GSM1974643_S3_H113C.CEL.gz
Reading in : celfiles//GSM1974644_S3_H114C.CEL.gz
Reading in : celfiles//GSM1974645_S3_H115.CEL.gz
Reading in : celfiles//GSM1974646_S3_H116C.CEL.gz
Reading in : celfiles//GSM1974647_S3_H117C.CEL.gz
Reading in : celfiles//GSM1974648_S3_H118C.CEL.gz
Reading in : celfiles//GSM1974649_S3_H119C.CEL.gz
Reading in : celfiles//GSM1974650_S3_H120C.CEL.gz
Reading in : celfiles//GSM1974651_S3_H121C.CEL.gz
Reading in : celfiles//GSM1974652_S3_H122C.CEL.gz
Reading in : celfiles//GSM1974653_S3_H123C.CEL.gz
Reading in : celfiles//GSM1974654_S3_H124C.CEL.gz
Reading in : celfiles//GSM1974655_S3_H125C.CEL.gz
Reading in : celfiles//GSM1974656_S3_H126C.CEL.gz
Reading in : celfiles//GSM1974657_S3_H127C.CEL.gz
Reading in : celfiles//GSM1974658_S3_H128C.CEL.gz
Reading in : celfiles//GSM1974659_S3_H129C.CEL.gz
Reading in : celfiles//GSM1974660_S3_H130.CEL.gz
Reading in : celfiles//GSM1974661_S3_H131.CEL.gz
Reading in : celfiles//GSM1974662_S3_H132.CEL.gz
Reading in : celfiles//GSM1974663_S3_H133D.CEL.gz
Reading in : celfiles//GSM1974664_S3_H134.CEL.gz
Reading in : celfiles//GSM1974665_S3_H135B.CEL.gz
Reading in : celfiles//GSM1974666_S3_H136B.CEL.gz
Reading in : celfiles//GSM1974667_S3_H137B.CEL.gz
Reading in : celfiles//GSM1974668_S3_H138.CEL.gz
Reading in : celfiles//GSM1974669_S3_H139.CEL.gz
Reading in : celfiles//GSM1974670_S3_H140.CEL.gz
Reading in : celfiles//GSM1974671_S3_H141.CEL.gz
Reading in : celfiles//GSM1974672_S3_H142.CEL.gz
Reading in : celfiles//GSM1974673_S3_H143.CEL.gz
Reading in : celfiles//GSM1974674_S3_H144.CEL.gz
Reading in : celfiles//GSM1974675_S3_H145.CEL.gz
Reading in : celfiles//GSM1974676_S3_H146.CEL.gz
Reading in : celfiles//GSM1974677_S3_H147.CEL.gz
Reading in : celfiles//GSM1974678_S3_H148.CEL.gz
Reading in : celfiles//GSM1974679_S3_H149.CEL.gz
Reading in : celfiles//GSM1974680_S3_H150.CEL.gz
Reading in : celfiles//GSM1974681_S3_H151.CEL.gz
Reading in : celfiles//GSM1974682_S3_H152.CEL.gz
Reading in : celfiles//GSM1974683_S3_H153.CEL.gz
Reading in : celfiles//GSM1974684_S3_H154.CEL.gz
Reading in : celfiles//GSM1974685_S3_H155.CEL.gz
Reading in : celfiles//GSM1974686_S3_H156.CEL.gz
Reading in : celfiles//GSM1974687_S3_H157.CEL.gz
Reading in : celfiles//GSM1974688_S3_H158.CEL.gz
Reading in : celfiles//GSM1974689_S3_H159.CEL.gz
Reading in : celfiles//GSM1974690_S3_H160.CEL.gz
Reading in : celfiles//GSM1974691_S3_H161.CEL.gz
Reading in : celfiles//GSM1974692_S3_H162.CEL.gz
Reading in : celfiles//GSM1974693_S3_H163.CEL.gz
Reading in : celfiles//GSM1974694_S3_H164C.CEL.gz
Reading in : celfiles//GSM1974695_S3_H165.CEL.gz
Reading in : celfiles//GSM1974696_S3_H166.CEL.gz
Reading in : celfiles//GSM1974697_S3_H167.CEL.gz
Reading in : celfiles//GSM1974698_S3_H168.CEL.gz
Reading in : celfiles//GSM1974699_S3_H170.CEL.gz
Reading in : celfiles//GSM1974700_S3_H171.CEL.gz
Reading in : celfiles//GSM1974701_S3_H172B.CEL.gz
Reading in : celfiles//GSM1974702_S3_H173.CEL.gz
Reading in : celfiles//GSM1974703_S3_H174.CEL.gz
Reading in : celfiles//GSM1974704_S3_H175B.CEL.gz
Reading in : celfiles//GSM1974705_S3_H176.CEL.gz
Reading in : celfiles//GSM1974706_S3_H177.CEL.gz
Reading in : celfiles//GSM1974707_S3_H178.CEL.gz
Reading in : celfiles//GSM1974708_S3_H179.CEL.gz
Reading in : celfiles//GSM1974709_S3_H180.CEL.gz
Reading in : celfiles//GSM1974710_S3_H181.CEL.gz
Reading in : celfiles//GSM1974711_S3_H182.CEL.gz
Reading in : celfiles//GSM1974712_S3_H183.CEL.gz
Reading in : celfiles//GSM1974713_S3_H184.CEL.gz
Reading in : celfiles//GSM1974714_S3_H185.CEL.gz
Reading in : celfiles//GSM1974715_S3_H186.CEL.gz
Reading in : celfiles//GSM1974716_S3_H187B.CEL.gz
Reading in : celfiles//GSM1974717_S3_H188.CEL.gz
Reading in : celfiles//GSM1974718_S3_H189.CEL.gz
Reading in : celfiles//GSM1974719_S3_H190.CEL.gz
Reading in : celfiles//GSM1974720_S3_H191B.CEL.gz
Reading in : celfiles//GSM1974721_S3_H193.CEL.gz
Reading in : celfiles//GSM1974722_S3_H194.CEL.gz
Reading in : celfiles//GSM1974723_S3_H196.CEL.gz
Reading in : celfiles//GSM1974724_S3_H197.CEL.gz
Reading in : celfiles//GSM1974725_S3_H198.CEL.gz
Reading in : celfiles//GSM1974726_S3_H199.CEL.gz
Reading in : celfiles//GSM1974727_S3_H200.CEL.gz
Reading in : celfiles//GSM1974728_S3_H201.CEL.gz
Reading in : celfiles//GSM1974729_S3_H202.CEL.gz
Reading in : celfiles//GSM1974730_S3_H203.CEL.gz
Reading in : celfiles//GSM1974731_S3_H204.CEL.gz
Reading in : celfiles//GSM1974732_S3_H205B.CEL.gz
Reading in : celfiles//GSM1974733_S3_H206B.CEL.gz
Reading in : celfiles//GSM1974734_S3_H207B.CEL.gz
Reading in : celfiles//GSM1974735_S3_H208B.CEL.gz
Reading in : celfiles//GSM1974736_S3_H209B.CEL.gz
Reading in : celfiles//GSM1974737_S3_H210B.CEL.gz
Reading in : celfiles//GSM1974738_S3_H211B.CEL.gz
Reading in : celfiles//GSM1974739_S3_H212.CEL.gz
Reading in : celfiles//GSM1974740_S3_H213.CEL.gz
Reading in : celfiles//GSM1974741_S3_H214.CEL.gz
Reading in : celfiles//GSM1974742_S3_H215.CEL.gz
Reading in : celfiles//GSM1974743_S3_H216.CEL.gz
Reading in : celfiles//GSM1974744_S3_H217.CEL.gz
Reading in : celfiles//GSM1974745_S3_H218.CEL.gz
Reading in : celfiles//GSM1974746_S3_H219.CEL.gz
Reading in : celfiles//GSM1974747_S3_H220.CEL.gz
Reading in : celfiles//GSM1974748_S3_H221.CEL.gz
Reading in : celfiles//GSM1974749_S3_H222.CEL.gz
Reading in : celfiles//GSM1974750_S3_H224.CEL.gz
Reading in : celfiles//GSM1974751_S3_H225B.CEL.gz
Reading in : celfiles//GSM1974752_S3_H226B.CEL.gz
Reading in : celfiles//GSM1974753_S3_H227B.CEL.gz
Reading in : celfiles//GSM1974754_S3_H228.CEL.gz
Reading in : celfiles//GSM1974755_S3_H229.CEL.gz
Reading in : celfiles//GSM1974756_S3_H230.CEL.gz
Reading in : celfiles//GSM1974757_S3_H93C.CEL.gz
Reading in : celfiles//GSM1974758_S3_H94C.CEL.gz
Reading in : celfiles//GSM1974759_S3_H95C.CEL.gz
Reading in : celfiles//GSM1974760_S3_H96C.CEL.gz
Reading in : celfiles//GSM1974761_S3_H97CC.CEL.gz
Reading in : celfiles//GSM1974762_S3_H98C.CEL.gz
Reading in : celfiles//GSM1974763_S3_H99C.CEL.gz
Reading in : celfiles//GSM1978883_S1_H11.CEL.gz
Reading in : celfiles//GSM1978884_S1_H12.CEL.gz
Reading in : celfiles//GSM1978885_S1_H13.CEL.gz
Reading in : celfiles//GSM1978886_S1_H15.CEL.gz
Reading in : celfiles//GSM1978887_S1_H16.CEL.gz
Reading in : celfiles//GSM1978888_S1_H17.CEL.gz
Reading in : celfiles//GSM1978889_S1_H18.CEL.gz
Reading in : celfiles//GSM1978890_S1_H1B.CEL.gz
Reading in : celfiles//GSM1978891_S1_H21.CEL.gz
Reading in : celfiles//GSM1978892_S1_H23.CEL.gz
Reading in : celfiles//GSM1978893_S1_H25.CEL.gz
Reading in : celfiles//GSM1978894_S1_H30.CEL.gz
Reading in : celfiles//GSM1978895_S1_H32.CEL.gz
Reading in : celfiles//GSM1978896_S1_H37.CEL.gz
Reading in : celfiles//GSM1978897_S1_H39.CEL.gz
Reading in : celfiles//GSM1978898_S1_H42.CEL.gz
Reading in : celfiles//GSM1978899_S1_H5.CEL.gz
Reading in : celfiles//GSM1978900_S3_H195.CEL.gz
Reading in : celfiles//GSM1978901_S3_H223.CEL.gz
Reading in : celfiles//GSM1978902_S4_H231.CEL.gz
Reading in : celfiles//GSM1978903_S4_H232.CEL.gz
Reading in : celfiles//GSM1978904_S4_H233.CEL.gz
Reading in : celfiles//GSM1978905_S4_H234.CEL.gz
Reading in : celfiles//GSM1978906_S4_H235.CEL.gz
Reading in : celfiles//GSM1978907_S4_H236.CEL.gz
Reading in : celfiles//GSM1978908_S4_H237.CEL.gz
Reading in : celfiles//GSM1978909_S4_H238.CEL.gz
Reading in : celfiles//GSM1978910_S4_H239.CEL.gz
Reading in : celfiles//GSM1978911_S4_H240.CEL.gz
Reading in : celfiles//GSM1978912_S4_H241.CEL.gz
Reading in : celfiles//GSM1978913_S4_H242.CEL.gz
Reading in : celfiles//GSM1978914_S4_H243.CEL.gz
Reading in : celfiles//GSM1978915_S4_H244.CEL.gz
Reading in : celfiles//GSM1978916_S4_H245.CEL.gz
Reading in : celfiles//GSM1978917_S4_H246.CEL.gz
Reading in : celfiles//GSM1978918_S4_H247.CEL.gz
Reading in : celfiles//GSM1978919_S4_H248.CEL.gz
Reading in : celfiles//GSM1978920_S4_H249.CEL.gz
Reading in : celfiles//GSM1978921_S4_H250.CEL.gz
Reading in : celfiles//GSM1978922_S4_H251.CEL.gz
Reading in : celfiles//GSM1978923_S4_H252.CEL.gz
Reading in : celfiles//GSM1978924_S4_H253.CEL.gz
Reading in : celfiles//GSM1978925_S4_H254.CEL.gz
Reading in : celfiles//GSM1978926_S4_H255.CEL.gz
Reading in : celfiles//GSM1978927_S4_H256.CEL.gz
Reading in : celfiles//GSM1978928_S4_H257B.CEL.gz
Reading in : celfiles//GSM1978929_S4_H258B.CEL.gz
Reading in : celfiles//GSM1978930_S4_H259.CEL.gz
Reading in : celfiles//GSM1978931_S4_H261.CEL.gz
Reading in : celfiles//GSM1978932_S4_H262.CEL.gz
Reading in : celfiles//GSM1978933_S4_H263.CEL.gz
Reading in : celfiles//GSM1978934_S4_H264.CEL.gz
Reading in : celfiles//GSM1978935_S4_H265.CEL.gz
Reading in : celfiles//GSM1978936_S4_H266.CEL.gz
Reading in : celfiles//GSM1978937_S4_H267.CEL.gz
Reading in : celfiles//GSM1978938_S4_H268.CEL.gz
Reading in : celfiles//GSM1978939_S4_H269.CEL.gz
Reading in : celfiles//GSM1978940_S4_H270.CEL.gz
Reading in : celfiles//GSM1978941_S4_H271.CEL.gz
Reading in : celfiles//GSM1978942_S4_H272.CEL.gz
Reading in : celfiles//GSM1978943_S4_H273.CEL.gz
Reading in : celfiles//GSM1978944_S4_H274.CEL.gz
Reading in : celfiles//GSM1978945_S4_H275B.CEL.gz
Reading in : celfiles//GSM1978946_S4_H276B.CEL.gz
Reading in : celfiles//GSM1978947_S4_H278.CEL.gz
Reading in : celfiles//GSM1978948_S4_H279B.CEL.gz
Reading in : celfiles//GSM1978949_S4_H280B.CEL.gz
Warning in read.celfiles(celFiles, phenoData = AnnotatedDataFrame(mdata_subset)) :
  'channel' automatically added to varMetadata in phenoData.
# saveRDS(object = rawData, "rawData.RDS")
rawData <- readRDS("rawData.RDS")

Looking at the dimensions of the raw expression matrix.

exprs(rawData) %>% dim()
[1] 1354896     265

Plotting some metadata attributes

Looking at the number of samples for triple negative status and for set.

mdata_subset %>% 
  count(triple_negative_status, set)
mdata_subset %>% 
  count(triple_negative_status) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(y = "dummy_group",
                         x = proportion,
                         fill = triple_negative_status)) +
    theme(axis.text.x = element_text(angle = 90, size = 7),
          axis.text.y = element_blank(),
          axis.ticks.y = element_blank(),
          axis.title.y = element_blank(),
          title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "horizontal", legend.position = "top") +
  geom_text(aes(y = 1, 
                x = proportion, 
                label = proportion, 
                group = triple_negative_status), 
            size = 5, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
  scale_fill_npg(name = "Triple Negative Status") +
  labs(x = "Proportion",
       title = str_wrap("Proportions of TNBC status values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_tnbc_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  count(triple_negative_status, pr) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(x = pr,
                         y = proportion,
                         fill = triple_negative_status)) +
    geom_text(aes(x = pr, 
                y = proportion, 
                label = proportion, 
                group = triple_negative_status), 
            size = 3, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
    theme(title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "vertical", legend.position = "right") +
  scale_fill_npg(name = "Triple Negative Status") +
  labs(x = "Progesterone Receptor Status",
       y = "Proportion",
       title = str_wrap("Proportions of progesterone receptor status values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_pr_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  count(triple_negative_status, er) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(x = er,
                         y = proportion,
                         fill = triple_negative_status)) +
      geom_text(aes(x = er, 
                y = proportion, 
                label = proportion, 
                group = triple_negative_status), 
            size = 3, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
    theme(title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "vertical", legend.position = "right") +
  scale_fill_npg(name = "Triple Negative Status") +
  labs(x = "Estrogen Receptor Status",
       y = "Proportion",
       title = str_wrap("Proportions of estrogen receptor status values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_er_proportion_barplot.png")
Saving 5.03 x 3.11 in image

mdata_subset %>% 
  count(triple_negative_status, her2) %>% 
  mutate(proportion = round(n/sum(n), 3)) %>% 
  ggplot() +
  geom_col(mapping = aes(x = her2,
                         y = proportion,
                         fill = triple_negative_status)) +
    geom_text(aes(x = her2, 
                y = proportion, 
                label = proportion, 
                group = triple_negative_status), 
            size = 3, 
            position = position_stack(vjust = 0.5), 
            color = "white") +
    theme(title = element_text(size = 10),
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          panel.background = element_blank(),
          axis.line = element_line(colour = "black", size = 0.5),
          legend.direction = "vertical", legend.position = "right") +
  scale_fill_npg(name = "Triple Negative Status") +
  labs(x = "HER2 Amplification Status",
       y = "Proportion",
       title = str_wrap("Proportions of HER2 amplification status values for GSE76275", 60))

ggsave(filename = "plots/exploration_plots/GSE76275_her2_proportion_barplot.png")
Saving 5.03 x 3.11 in image

list("ER" = mdata_subset$geo_accession[mdata_subset$er == "Positive"],
     "PR" = mdata_subset$geo_accession[mdata_subset$pr == "Positive"],
     "HER2" = mdata_subset$geo_accession[mdata_subset$her2 == "Positive"]) %>%
  fromList(.) %>% 
  upset(., c("ER", "PR", "HER2"), width_ratio = 0.2) +
  ggtitle(str_wrap("Upset plot for different combinations of ER, PR, and HER2 status in the non-TNBC samples in GSE76275", 60)) +
  theme(title = element_text(size = 7))

ggsave("plots/exploration_plots/GSE76275_nonTNBC_upset.png")
Saving 5.03 x 3.11 in image

Reordering the columns in the metadata.

mdata <- select(mdata, geo_accession, everything())
head(mdata)

Performing RMA

Using regular RMA on data (without separating by class)

res_1 <- rma(rawData)
Loading required package: RSQLite
Loading required package: DBI
Background correcting
Normalizing
Calculating Expression
# saveRDS(object = res_1, "res_1.RDS")
res_1 <- readRDS("res_1.RDS")
exprs(res_1) %>% 
  dim()
[1] 54675   265
exprs(res_1)[1:5, 1:5]
          GSM1974566 GSM1974567 GSM1974568 GSM1974569
1007_s_at  10.754163  11.361803   9.690693  10.157010
1053_at     8.625663   9.011796   7.854072   7.925281
117_at      7.333973   7.385199   7.466878   8.048563
121_at      9.077887   8.782080   8.885189   8.775218
1255_g_at   4.656331   4.635625   4.536114   4.626950
          GSM1974570
1007_s_at  10.597699
1053_at     8.456781
117_at      7.581895
121_at      8.752407
1255_g_at   5.454820

Performing class-specific RMA by reading in the expression sets separately

Getting lists of the TNBC samples and the non-TNBC samples.

tnbc_samples <- mdata_subset %>% 
  filter(triple_negative_status == "TN") %>% 
  select(geo_accession) %>% 
  unlist(use.names = F) %>% 
  as.character()

head(tnbc_samples)
[1] "GSM1974566" "GSM1974567" "GSM1974568" "GSM1974569"
[5] "GSM1974570" "GSM1974571"
nontnbc_samples <- mdata_subset %>% 
  filter(triple_negative_status == "not TN") %>% 
  select(geo_accession) %>% 
  unlist(use.names = F) %>% 
  as.character()

head(nontnbc_samples)
[1] "GSM1978883" "GSM1978884" "GSM1978885" "GSM1978886"
[5] "GSM1978887" "GSM1978888"

Creating different metadata tables for TNBC and nonTNBC.

mdata_subset_tnbc <- mdata_subset[tnbc_samples, ]
dim(mdata_subset_tnbc)
[1] 198  12
mdata_subset_nontnbc <- mdata_subset[nontnbc_samples, ]
dim(mdata_subset_nontnbc)
[1] 67 12

Reading in the TNBC files.

# rawData_tnbc <- read.celfiles(filenames = celFiles[tnbc_samples], 
#                               phenoData = AnnotatedDataFrame(mdata_subset_tnbc))
# 
# rawData_tnbc
# saveRDS(rawData_tnbc, file = "rawData_tnbc.RDS")
rawData_tnbc <- readRDS(file = "rawData_tnbc.RDS")
rawData_tnbc
ExpressionFeatureSet (storageMode: lockedEnvironment)
assayData: 1354896 features, 198 samples 
  element names: exprs 
protocolData
  rowNames: GSM1974566 GSM1974567 ... GSM1974763 (198 total)
  varLabels: exprs dates
  varMetadata: labelDescription channel
phenoData
  rowNames: GSM1974566 GSM1974567 ... GSM1974763 (198 total)
  varLabels: geo_accession title ... age_years (11 total)
  varMetadata: labelDescription channel
featureData: none
experimentData: use 'experimentData(object)'
Annotation: pd.hg.u133.plus.2 
Loading required package: pd.hg.u133.plus.2
Loading required package: RSQLite
Loading required package: DBI

Reading in the nonTNBC files.

# rawData_nontnbc <- read.celfiles(filenames = celFiles[nontnbc_samples], 
#                               phenoData = AnnotatedDataFrame(mdata_subset_nontnbc))
# 
# rawData_nontnbc
# saveRDS(rawData_nontnbc, file = "rawData_nontnbc.RDS")
rawData_nontnbc <- readRDS(file = "rawData_nontnbc.RDS")
rawData_nontnbc
ExpressionFeatureSet (storageMode: lockedEnvironment)
assayData: 1354896 features, 67 samples 
  element names: exprs 
protocolData
  rowNames: GSM1978883 GSM1978884 ... GSM1978949 (67
    total)
  varLabels: exprs dates
  varMetadata: labelDescription channel
phenoData
  rowNames: GSM1978883 GSM1978884 ... GSM1978949 (67
    total)
  varLabels: geo_accession title ... age_years (11
    total)
  varMetadata: labelDescription channel
featureData: none
experimentData: use 'experimentData(object)'
Annotation: pd.hg.u133.plus.2 

Performing RMA on TNBC data.

# res_tnbc <- rma(rawData_tnbc)
# saveRDS(res_tnbc, file = "res_tnbc.RDS")
res_tnbc <- readRDS(file = "res_tnbc.RDS")

Performing RMA on nonTNBC data.

# res_nontnbc <- rma(rawData_nontnbc)
# saveRDS(res_nontnbc, file = "res_nontnbc.RDS")
res_nontnbc <- readRDS(file = "res_nontnbc.RDS")

Combining the expression matrices of TNBC and nonTNBC data after separate RMA.

res_joint <- cbind(exprs(res_tnbc), exprs(res_nontnbc))
res_joint[1:5, 1:5]
          GSM1974566 GSM1974567 GSM1974568 GSM1974569
1007_s_at  10.703231  11.345325   9.732361  10.175704
1053_at     8.605269   9.019553   7.794234   7.945029
117_at      7.360884   7.373951   7.481344   8.047586
121_at      9.100729   8.776369   8.860402   8.755606
1255_g_at   4.664492   4.628692   4.569530   4.627230
          GSM1974570
1007_s_at  10.576463
1053_at     8.497373
117_at      7.530427
121_at      8.766560
1255_g_at   5.467292

Saving certain CSV files for everyone else to refer to

Saving the joint expression matrix from class-specific QN.

res_joint %>% 
  as_tibble(rownames = "probe_id") %>% 
  write_csv("dataframe_files/post_classQN_expression.csv")
Error: Cannot open file for writing:
* 'dataframe_files/post_classQN_expression.csv'

Saving a subset of the metadata that I think is relevant.

mdata_subset %>% 
  write_csv("dataframe_files/metadata_subset.csv")

Saving the TNBC and nonTNBC metadata separately, just in case.

mdata_subset_tnbc %>% 
  write_csv("dataframe_files/metadata_subset_tnbc.csv")
mdata_subset_nontnbc %>% 
  write_csv("dataframe_files/metadata_subset_nontnbc.csv")

Getting sample-specific boxplots

Sample specific boxplots for regular RMA QN

res_1_df_long <- res_1 %>%
  exprs() %>% 
  as_tibble(rownames = "probeID") %>% 
  pivot_longer(cols = all_of(c(tnbc_samples, nontnbc_samples)), names_to = "sample_id", 
               values_to = "intensity") %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession"))
# saveRDS(object = res_1_df_long, "res_1_df_long.RDS")
res_1_df_long <- readRDS("res_1_df_long.RDS")
p2 <- res_1_df_long %>% 
  ggplot() +
  geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, color = set), outlier.size = 0.2)
p2 <- p2 + labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots for global quantile normalization for GSE76275", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  theme_light() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        title = element_text(size = 10),
        legend.position = "top", 
        legend.direction = "horizontal")
p2

NA
ggsave("plots/exploration_plots/GSE76275_post_regQN_boxplots.png", 
       p2, 
       units = "cm", 
       width = 30, 
       height = 10)
rm(res_1_df_long)

Sample specific boxplots for classQN

res_joint_df_long <- res_joint %>% 
  as_tibble(rownames = "probeID") %>% 
  pivot_longer(cols = all_of(c(tnbc_samples, nontnbc_samples)), names_to = "sample_id", 
               values_to = "intensity")
  
# saveRDS(object = res_joint_df_long, "res_joint_df_long.RDS")
res_joint_df_long <- readRDS("res_joint_df_long.RDS")
p1 <- res_joint_df_long %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession")) %>% 
  mutate(sample_id = factor(sample_id)) %>% 
  ggplot() +
   geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, color = set), outlier.size = 0.2)
p1 <- p1 + labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots for class-specific quantile normalization for GSE76275", 60)) +
scale_color_npg(name = "Triple Negative Status") +
  theme_light() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        title = element_text(size = 10),
        legend.position = "top", 
        legend.direction = "horizontal")

p1 

ggsave("plots/exploration_plots/GSE76275_post_classQN_boxplots.png", 
       p1, 
       units = "cm", width = 20, height = 10)
res_joint_df_long %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession")) %>% 
  mutate(sample_id = factor(sample_id)) %>% 
  ggplot() +
   geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, fill = set), outlier.size = 0.2) +
labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots for class-specific quantile normalization for GSE76275", 60)) +
scale_fill_npg(name = "Triple Negative Status") +
  theme_light() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        title = element_text(size = 10),
        legend.position = "right", 
        legend.direction = "vertical")

ggsave("plots/exploration_plots/GSE76275_post_classQN_boxplots_bad.png", units = "cm", width = 20, height = 10)

Performing PCA

Custom functions

Function to create an annotated data frame by combining PC scores as well as metadata: useful for ggplot visualization.

get_pca_annot_df <- function(pca.obj, sample_id_col, mdata_df){
  ind_scores <- pca.obj$x
  ind_scores_reordered <- ind_scores[match(rownames(ind_scores), mdata_df[[sample_id_col]]), ] %>% 
    as_tibble(rownames = sample_id_col) %>% 
    mutate(filename = factor(!!sym(sample_id_col)))
  ind_scores_annot <- left_join(ind_scores_reordered, y = mdata_df, by = sample_id_col) %>% 
  select(all_of(colnames(mdata_subset)), contains("PC"))
  return(ind_scores_annot)
}

Performing PCA on regular RMA data

# pca.res_1 <- res_1 %>% 
#   exprs() %>% 
#   t() %>% 
#   prcomp(center = TRUE, scale = TRUE)
# saveRDS(pca.res_1, "pca_res1.RDS")
pca.res_1 <- readRDS("pca_res1.RDS")

Getting the annotated data frame for the PCA.

pca.res_1.annot_df <- get_pca_annot_df(pca.obj = pca.res_1, sample_id_col = "geo_accession", mdata_df= mdata_subset)
head(pca.res_1.annot_df)

Visualizing PCA results

Looking at the variance explained by the first 10 PCs.

fviz_eig(pca.res_1) +
  labs(x = "Principal Component", 
       title = str_wrap("Scree plot for the first 10 principal components for global RMA-normalized data for GSE76275", 60)) +
    theme(title = element_text(size = 10), 
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_wholeQN_scree.png", bg = "white")
Saving 5.03 x 3.11 in image

Superimposing variables in data upon sample PCA scores. The PCA does not seem to separate the TNBC and nonTNBC samples that well when regular RMA is performed.

ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by triple negative status for GSE76275 after global quantile normalization", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))


ggsave("plots/exploration_plots/PCA_wholeQN_TNBC_status.png", bg = "white")
Saving 5.03 x 3.11 in image

NA

The samples do not seem to separate well by set either.

ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = set)) +
  ggtitle(str_wrap("Samples in first two PCs, coloured by set (discovery or validation) for GSE76275 after global quantile normalization", 60)) +
  scale_color_aaas(name = "Sample Set") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_wholeQN_set.png", bg = "white")
Saving 5.03 x 3.11 in image

There does not seem to be too strong of a batch effect according to submission date.

ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = submission_date)) +
    ggtitle("Samples in first two PCs, \ncoloured by submission date for whole QN") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_wholeQN_set.png")
ggplot(pca.res_1.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = tnbc_subtype)) +
    ggtitle("Samples in first two PCs, \ncoloured by tnbc_subtype for whole QN") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5), legend.position = "right", legend.direction = "vertical", legend.key.width = unit(x = 0.5, units = "cm")) 

Performing PCA on separately-performed RMA data

pca.res_joint <- res_joint %>% 
  t() %>% 
  prcomp(center = TRUE, scale = TRUE)
# saveRDS(pca.res_joint, "pca_res_joint.RDS")
pca.res_joint <- readRDS("pca_res_joint.RDS")

Getting the annotated data frame for the PCA.

pca.res_joint.annot_df <- get_pca_annot_df(pca.obj = pca.res_joint, sample_id_col = "geo_accession", mdata_df= mdata_subset)
head(pca.res_joint.annot_df)

Visualizing PCA results

Looking at the variance explained by the first 10 PCs.

fviz_eig(pca.res_joint) +
  labs(x = "Principal Component", 
       title = str_wrap("Scree plot for the first 10 principal components for class-specific RMA-normalized data for GSE76275", 60)) +
    theme(title = element_text(size = 10), 
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_scree.png")
Saving 5.03 x 3.11 in image

Superimposing variables in data upon sample PCA scores. The PCA does separate the TNBC and nonTNBC samples well when class-specific RMA is performed.

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by triple negative status for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Triple Negative Status") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_status.png")
Saving 5.03 x 3.11 in image

The validation nonTNBC samples are separated from the discovery TNBC and validation TNBC samples.

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = set)) +
  ggtitle(str_wrap("Samples in first two PCs, coloured by set (discovery or validation) for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_aaas(name = "Sample Set") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
        title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_set.png")
Saving 5.03 x 3.11 in image

Submission date is perfectly confounded with TNBC status. May or may not be batch effects.

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = submission_date)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by submission date for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Submission Date") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_date.png")
Saving 5.03 x 3.11 in image

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2, colour = tnbc_subtype)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by subtype of TNBC for GSE76275 after class-specific quantile normalization", 60)) +
   scale_color_nejm(name = "TNBC Subtype") +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5),
        legend.text = element_text(size = 5), legend.key.height = unit(x = 0.3, units = "cm"), legend.key.width = unit(x = 0.3, units = "cm")) +
  guides(color = guide_legend(override.aes = list(size = 1)))

ggsave("plots/exploration_plots/PCA_classQN_TNBC_subtype.png")
Saving 5.03 x 3.11 in image

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2,
                           colour = factor(pr), shape = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by progesterone receptor status for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Progesterone Receptor Status") +
  scale_shape_manual(name = "Triple Negative Status", values = c("TN" = 17, "not TN" = 1)) +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5),
        legend.title = element_text(size = 7),
        legend.text = element_text(size = 5), 
        legend.key.height = unit(x = 0.3, units = "cm"),
        legend.key.width = unit(x = 0.3, units = "cm")) +
    guides(color = guide_legend(override.aes = list(size = 1)))


ggsave("plots/exploration_plots/PCA_classQN_pr_GSE76275.png", bg= "white")
Saving 5.03 x 3.11 in image

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2,
                           colour = factor(her2), shape = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by HER2 amplification status for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_nejm(name = "HER2 Amplification Status") +
  scale_shape_manual(name = "Triple Negative Status", values = c("TN" = 17, "not TN" = 1)) +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5),
        legend.title = element_text(size = 7),
        legend.text = element_text(size = 5), 
        legend.key.height = unit(x = 0.3, units = "cm"),
        legend.key.width = unit(x = 0.3, units = "cm")) + 
      guides(color = guide_legend(override.aes = list(size = 1)))

ggsave("plots/exploration_plots/PCA_classQN_her2_GSE76275.png", bg = "white")
Saving 5.03 x 3.11 in image

ggplot(pca.res_joint.annot_df) + 
  geom_point(mapping = aes(x = PC1, y = PC2,
                           colour = factor(er), shape = triple_negative_status)) +
    ggtitle(str_wrap("Samples in first two PCs, coloured by estrogen receptor status for GSE76275 after class-specific quantile normalization", 60)) +
  scale_color_npg(name = "Estrogen Receptor Status") +
  scale_shape_manual(name = "Triple Negative Status", values = c("TN" = 17, "not TN" = 1)) +
  guides(colour = guide_legend(override.aes = list(size= 4))) +
  theme(axis.text.x = element_text(angle = 90, size = 7),
               title = element_text(size = 10),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black", size = 0.5),
        legend.title = element_text(size = 7),
        legend.text = element_text(size = 5), 
        legend.key.height = unit(x = 0.3, units = "cm"),
        legend.key.width = unit(x = 0.3, units = "cm")) + 
      guides(color = guide_legend(override.aes = list(size = 1)))


ggsave("plots/exploration_plots/PCA_classQN_er_GSE76275.png")
Saving 5.03 x 3.11 in image

Performing hierarchical clustering

Getting distances

perform_min_max <- function(x){
  mm_transformation <- preProcess(x, method = "range")
  rescaled <- predict(mm_transformation, x)
  return(rescaled)
}

Getting distances after performing min max normalization.

# res_1_dists <- exprs(res_1) %>% 
#   t() %>% 
#   perform_min_max() %>% 
#   dist(method = "euclidean")
  
# saveRDS(res_1_dists, "res_1_dists.RDS")
res_1_dists <- readRDS("res_1_dists.RDS")
# res_joint_dists <- res_joint %>% 
#     t() %>% 
#   perform_min_max() %>% 
#   dist(method = "euclidean")
  
# saveRDS(res_joint_dists, "res_joint_dists.RDS")
res_joint_dists <- readRDS("res_joint_dists.RDS")

Using dendrograms

res_1_dend <- res_1_dists %>% 
  hclust() %>% 
  as.dendrogram()
res_joint_dend <- res_joint_dists %>%
  hclust() %>% 
  as.dendrogram()
library(dendextend)
# res_1_dend %>% 
#   labels()
# res_1_dend %>% 
#   order.dendrogram()
# (res_1 %>% 
#   exprs() %>% 
#   colnames())[28]
  
res_1_dend_laborder <- res_1_dend %>% 
  labels()
mycolors <- ifelse(mdata_subset[res_1_dend_laborder, ]$triple_negative_status == "TN", "forestgreen", "maroon")
par(mar = c(10,2,1,1))
res_1_dend %>% 
  set("labels_cex", 0.1) %>% 
  plot()

colored_bars(colors = mycolors, dend = res_1_dend, rowLabels = "TN Status", add = TRUE)
res_joint_dend_laborder <- res_joint_dend %>% 
  labels()
mycolors <- ifelse(mdata_subset[res_joint_dend_laborder, ]$triple_negative_status == "TN", "forestgreen", "maroon")
par(mar = c(10,2,1,1))
res_joint_dend %>% 
  set("labels_cex", 0.1) %>% 
  plot()

colored_bars(colors = mycolors, dend = res_joint_dend, rowLabels = "TN Status", add = TRUE)

Using heatmaps

Function to process distance object into a distance matrix for heatmap visualization.

get_distmat <- function(x){
  distmat <- as.matrix(x)
  colnames(distmat) <- NULL
  diag(distmat) <- NA
  return(distmat)
}
row_annot <- mdata_subset %>% 
  mutate(er = factor(er), pr = factor(pr), her2 = factor(her2)) %>% 
  select(submission_date, triple_negative_status, pr, er, her2) %>% 
  rename(`TN status` = triple_negative_status)

head(row_annot)
set.seed(5)
row_colours <- list( "TN status" = c("darkgoldenrod4", "darkmagenta"), 
                     "submission_date" = pal_nejm()(2),
                     "pr" = pal_lancet()(9)[1:2], 
                     "er" = pal_lancet()(9)[8:9],
                     "her2" = pal_lancet()(9)[5:7])

names(row_colours[["TN status"]]) <- as.character(unique(row_annot[["TN status"]]))
names(row_colours$pr) <- as.character(unique(row_annot$pr))
names(row_colours$er) <- as.character(unique(row_annot$er))
names(row_colours$her2) <- as.character(unique(row_annot$her2))
names(row_colours$submission_date) <- as.character(unique(row_annot$submission_date))
str(row_colours)
List of 5
 $ TN status      : Named chr [1:2] "darkgoldenrod4" "darkmagenta"
  ..- attr(*, "names")= chr [1:2] "TN" "not TN"
 $ submission_date: Named chr [1:2] "#BC3C29FF" "#0072B5FF"
  ..- attr(*, "names")= chr [1:2] "Dec 17 2015" "Dec 22 2015"
 $ pr             : Named chr [1:2] "#00468BFF" "#ED0000FF"
  ..- attr(*, "names")= chr [1:2] "Negative" "Positive"
 $ er             : Named chr [1:2] "#ADB6B6FF" "#1B1919FF"
  ..- attr(*, "names")= chr [1:2] "Negative" "Positive"
 $ her2           : Named chr [1:3] "#925E9FFF" "#FDAF91FF" "#AD002AFF"
  ..- attr(*, "names")= chr [1:3] "Negative" "Positive" "Not Available"
my_colours <-  viridis(265^2, begin = 1, end = 0)
res_joint_dists %>% 
  get_distmat() %>% 
pheatmap(.,
         color = my_colours,
         annotation_row = row_annot,
         annotation_colors = row_colours,
         show_colnames = F,
         show_rownames = F,
         cutree_rows = 2,
         cutree_cols = 2,
         main = str_wrap("Heatmap of sample distances for class-specific QN expression matrix for GSE76275", 60),
         legend_labels = c("small distance", "large distance"),
         legend_breaks = c(min(., na.rm = TRUE), 
                         max(., na.rm = TRUE)), 
          filename = "plots/exploration_plots/classQN_clustering_heatmap_GSE76275.png")

Performing SVA

Performing SVA on regular RMA data

full_mod <- mdata_subset %>% 
  select(geo_accession, triple_negative_status) %>% 
  arrange(triple_negative_status) %>% 
  model.matrix(~triple_negative_status, data = .)

head(full_mod)
           (Intercept) triple_negative_statusTN
GSM1978883           1                        0
GSM1978884           1                        0
GSM1978885           1                        0
GSM1978886           1                        0
GSM1978887           1                        0
GSM1978888           1                        0
red_mod <- model.matrix(~1, data = mdata_subset)

head(red_mod)
           (Intercept)
GSM1974566           1
GSM1974567           1
GSM1974568           1
GSM1974569           1
GSM1974570           1
GSM1974571           1

Get number of significant surrogate variables.

n.sv.wholeQN <- num.sv(exprs(res_1), full_mod, method="leek")
n.sv.wholeQN
[1] 0
svobj.wholeQN <- sva(exprs(res_1), mod = full_mod, mod0 = red_mod, n.sv = 1)
sv_df.wholeQN <- tibble("geo_accession" = colnames(exprs(res_1)), "sv" = svobj.wholeQN$sv)

head(sv_df.wholeQN)
left_join(sv_df.wholeQN, mdata, by = "geo_accession") %>% 
  mutate(index = 5) %>% 
  ggplot() +
  # geom_col(mapping = aes(y = fct_reorder(geo_accession, sv, .fun = function(x){x}), x = sv, fill = set)) +
  geom_boxplot(mapping = aes(x = submission_date, y = sv, fill = set)) +
  theme_light() +
  labs(y = "Surrogate Variable Value", title = "Distribution of latent variable estimated by SVA for different grouping factors")
  

# ggsave("plots/exploration_plots/sva_grouping_normalRMA.png")

Performing SVA on class-specific quantile normalized data

Create full model matrix.

full_mod <- mdata_subset %>% 
  select(geo_accession, triple_negative_status) %>% 
  arrange(triple_negative_status) %>% 
  model.matrix(~triple_negative_status, data = .)

head(full_mod)
           (Intercept) triple_negative_statusTN
GSM1978883           1                        0
GSM1978884           1                        0
GSM1978885           1                        0
GSM1978886           1                        0
GSM1978887           1                        0
GSM1978888           1                        0

Create reduced model matrix.

red_mod <- model.matrix(~1, data = mdata_subset)

head(red_mod)
           (Intercept)
GSM1974566           1
GSM1974567           1
GSM1974568           1
GSM1974569           1
GSM1974570           1
GSM1974571           1

Get number of significant surrogate variables.

n.sv.classQN <- num.sv(res_joint, full_mod, method="leek")
n.sv.classQN
[1] 1

Perform SVA on classQN-normalized expression matrix.

svobj.classQN <- sva(res_joint, mod = full_mod, mod0 = red_mod, n.sv = n.sv.classQN)
Number of significant surrogate variables is:  1 
Iteration (out of 5 ):1  2  3  4  5  
sv_df.classQN <- tibble("geo_accession" = colnames(res_joint), "sv" = svobj.classQN$sv)

head(sv_df.classQN)
saveRDS(sv_df.classQN, "sv_df_classQN.RDS")
# sv_df.classQN <- readRDS("sv_df_classQN.RDS")
sv_df.classQN %>% 
ggplot() +
  geom_boxplot(mapping = aes(x = submission_date, y = sv, fill = er)) +
  # geom_point(mapping = aes(x = submission_date, y = sv, color = er)) +
  theme_light() 

  # labs(y = "Surrogate Variable Value", title = "Distribution of latent variable estimated by SVA for different grouping factors")
  

# ggsave("plots/exploration_plots/sva_grouping_classQN.png")

Trying to see if the SVA estimates a batch when QN is not applied

In this attempt, I perform no quantile normalization while performing RMA. If QN has not been performed and a surrogate variable shows up that corresponds to batch, batch effects are probably present.

rawData.summary <- rma(rawData, background = TRUE, normalize = FALSE)
rawData.summary_df_long <- rawData.summary %>% 
  exprs() %>% 
  as_tibble(rownames = "probeID") %>% 
  pivot_longer(cols = all_of(c(tnbc_samples, nontnbc_samples)), names_to = "sample_id", 
               values_to = "intensity") %>% 
  left_join(., mdata_subset, by = c("sample_id" = "geo_accession"))
p3 <- rawData.summary_df_long %>% 
  ggplot() +
  geom_boxplot(mapping = aes(x = reorder(sample_id, as.numeric(set)), y = intensity, 
                             color = set)) +
  labs(x = "samples", 
       title = str_wrap("Sample-wise log2 intensity boxplots in the absence of QN", 60)) +
  scale_color_npg() +
  theme(axis.text.x = element_blank())

p3
ggsave("plots/exploration_plots/GSE76275_noQN_boxplots.png", 
       p3, 
       units = "cm", width = 30, height = 10)

Getting the number of surrogate variables in the absence of quantile normalization.

n.sv.nonorm <- num.sv(exprs(rawData.summary), full_mod, method="leek")

There is one surrogate variable present in the absence of QN.

n.sv.nonorm
svobj.nonorm <- sva(exprs(rawData.summary), mod = full_mod, mod0 = red_mod, n.sv = n.sv.nonorm)
sv_df.nonorm <- tibble("geo_accession" = colnames(exprs(rawData.summary)), "sv" = svobj.nonorm$sv)

head(sv_df.nonorm)
left_join(sv_df.nonorm, mdata, by = "geo_accession") %>% 
  mutate(index = 5) %>% 
  ggplot() +
  # geom_col(mapping = aes(y = fct_reorder(geo_accession, sv, .fun = function(x){x}), x = sv, fill = set)) +
  geom_boxplot(mapping = aes(x = submission_date, y = sv, fill = set)) +
  theme_light() +
  labs(y = "Surrogate Variable Value", 
       title = str_wrap("Distribution of latent variable estimated by SVA for different grouping factors", 60))

ggsave("plots/exploration_plots/sva_grouping_noQN.png")
LS0tCnRpdGxlOiAiRXhwbG9yZSBHU0U3NjI3NSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMgTG9hZGluZyBsaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KEdFT3F1ZXJ5KQpsaWJyYXJ5KG9saWdvKQpsaWJyYXJ5KHN2YSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dzY2kpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShkZW5kZXh0ZW5kKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KFVwU2V0UikKbGlicmFyeShDb21wbGV4VXBzZXQpCmBgYAoKCiMgQ3VzdG9tIGZ1bmN0aW9ucwoKYGBge3J9CiMgZ2l2ZW4gYSBtYXRyaXgsIHBlcmZvcm0gbWluLW1heCBzY2FsaW5nIG9uIGl0cyBjb2x1bW5zCm1pbl9tYXhfbWF0IDwtIGZ1bmN0aW9uKG1hdCl7CiAgbWF0X3Jlc2NhbGVkIDwtIGFwcGx5KG1hdCwgMiwgZnVuY3Rpb24odil7CiAgICB2X3JhbmdlIDwtIHJhbmdlKHYpCiAgICBuYW1lcyh2X3JhbmdlKSA8LSBjKCJtaW5pbXVtIiwgIm1heGltdW0iKQogICAgcmFuZ2VfZGlmZmVyZW5jZSA8LSB2X3JhbmdlWyJtYXhpbXVtIl0gLSB2X3JhbmdlWyJtaW5pbXVtIl0KICAgIHJlc2NhbGVkIDwtICh2IC0gdl9yYW5nZVsibWluaW11bSJdKS9yYW5nZV9kaWZmZXJlbmNlCiAgICByZXR1cm4ocmVzY2FsZWQpCiAgfSkKICByZXR1cm4obWF0X3Jlc2NhbGVkKQp9CmBgYAoKIyBHZXR0aW5nIGRhdGEgZnJvbSBHRU9xdWVyeQoKYGBge3J9CiMgZ2VvZGF0YSA8LSBHRU9xdWVyeTo6Z2V0R0VPKEdFTyA9ICJHU0U3NjI3NSIsIGRlc3RkaXIgPSAiLi90ZW1wZmlsZXMiKQojIGdlb2RhdGEgPC0gR0VPcXVlcnk6OmdldEdFTyhmaWxlbmFtZSA9ICIuL3RlbXBmaWxlcy9HU0U3NjI3NV9zZXJpZXNfbWF0cml4LnR4dC5neiIpCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMoZ2VvZGF0YSwgImdlb2RhdGEuUkRTIikKZ2VvZGF0YSA8LSByZWFkUkRTKCJnZW9kYXRhLlJEUyIpCmBgYAoKCmBgYHtyfQojIG1kYXRhIDwtIGdlb2RhdGEgJT4lIAojICAgcGx1Y2soMSkgJT4lIAojICAgcGhlbm9EYXRhKCkgJT4lCiMgICBwRGF0YSgpICU+JSBhc190aWJibGUoKQpgYGAKCgpgYGB7cn0KIyBmZWF0dXJlX2RhdGEgPC0gZ2VvZGF0YSAlPiUgCiMgICBwbHVjaygxKSAlPiUgCiMgICBmZWF0dXJlRGF0YSgpCiAgCmBgYAoKCmBgYHtyfQojIHdyaXRlX2NzdihtZGF0YSwgInJhd19tZGF0YS5jc3YiKQptZGF0YSA8LSByZWFkX2NzdigicmF3X21kYXRhLmNzdiIpCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMoZmVhdHVyZV9kYXRhLCAiZmVhdHVyZURhdGEuUkRTIikKIyBmZWF0dXJlX2RhdGEgPC0gcmVhZFJEUygiZmVhdHVyZURhdGEuUkRTIikKYGBgCgojIEluc3BlY3RpbmcgYW5kIGNsZWFuaW5nIHRoZSBtZXRhZGF0YQoKYGBge3J9Cm1kYXRhICU+JSAKICBnbGltcHNlKCkKYGBgCgoKYGBge3J9Cm1kYXRhIDwtIG1kYXRhICU+JSAKICBzZWxlY3QodGl0bGUsIGNvbnRhaW5zKCJkYXRlIiksIGdlb19hY2Nlc3Npb24sIGNvbnRhaW5zKCI6Y2gxIikpCgpjb2xuYW1lcyhtZGF0YSkKICAKYGBgCgoKYGBge3J9CmNuYW1lcyA8LSBjb2xuYW1lcyhtZGF0YSkKYGBgCgoKYGBge3J9CmNuYW1lc19wcm9jZXNzZWQgPC0gc3RyX3NwbGl0KGNuYW1lcywgcGF0dGVybiA9ICI6IikgJT4lIAogIG1hcF9jaHIofnsueFtbMV1dfSkgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiICIsICJfIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiLSIsICJfIikgJT4lIAogIHN0cl9yZW1vdmVfYWxsKCJcXCh8XFwpfCwiKQoKY25hbWVzX3Byb2Nlc3NlZApgYGAKCgpgYGB7cn0KY29sbmFtZXMobWRhdGEpIDwtIGNuYW1lc19wcm9jZXNzZWQKcm0oY25hbWVzLCBjbmFtZXNfcHJvY2Vzc2VkKQpgYGAKCgpgYGB7cn0KZ2xpbXBzZShtZGF0YSkKYGBgCgpgYGB7cn0KbWRhdGEgPC0gbWRhdGEgJT4lIAogIG11dGF0ZShoZXIyID0gaWZfZWxzZSghaXMubmEoaGVyMiksIGhlcjIsICJOb3QgQXZhaWxhYmxlIikpICU+JSAKICBtdXRhdGUoZXIgPSBmYWN0b3IoZXIsIGxldmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpLCAKICAgICAgICAgcHIgPSBmYWN0b3IocHIsIGxldmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpLCAKICAgICAgICAgaGVyMiA9IGZhY3RvcihoZXIyLCBsZXZlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIsICJOb3QgQXZhaWxhYmxlIikpKSAlPiUgCiAgc2VsZWN0KGdlb19hY2Nlc3Npb24sIGV2ZXJ5dGhpbmcoKSkKaGVhZChtZGF0YSkgCmBgYAoKIyBSZWFkaW5nIGluIHJhdyBwcm9iZSBpbnRlbnNpdHkgZGF0YQoKQ2VsZmlsZXMgZG93bmxvYWRlZCBmcm9tIEdFTyBhbmQga2VwdCB0aGUgZm9sZGVyIGNlbGZpbGVzLwoKYGBge3J9CmNlbEZpbGVzIDwtIGxpc3QuY2VsZmlsZXMoJ2NlbGZpbGVzLycsIGZ1bGwubmFtZXMgPSBUUlVFLCBsaXN0R3ppcHBlZCA9IFRSVUUpCmNlbEZpbGVzICU+JSBoZWFkKCkKYGBgCgoKCmBgYHtyfQpuYW1lcyhjZWxGaWxlcykgPC0gY2VsRmlsZXMgJT4lIAogIGJhc2VuYW1lKCkgJT4lIAogIHN0cl9zcGxpdCgiXFwuIikgJT4lIAogIG1hcF9jaHIofnsueFsxXX0pICU+JSAKICBzdHJfc3BsaXQoIl8iKSAlPiUgCiAgbWFwX2Nocih+ey54WzFdfSkgCgpoZWFkKGNlbEZpbGVzKQpgYGAKClJlYXJyYW5naW5nIHJvd3Mgb2YgbWV0YWRhdGEgdG8gbWF0Y2ggb3JkZXIgb2Ygc2FtcGxlcyBpbiBgY2VsRmlsZXNgLgoKYGBge3J9Cm1kYXRhIDwtIG1kYXRhW21hdGNoKG1kYXRhJGdlb19hY2Nlc3Npb24sIG5hbWVzKGNlbEZpbGVzKSksIF0KYGBgCgoKR2V0dGluZyBvbmx5IHRoZSByZWxldmFudCB2YXJpYWJsZXMgZnJvbSB0aGUgbWV0YWRhdGEuCgpgYGB7cn0KbWRhdGFfc3Vic2V0IDwtIG1kYXRhICU+JQogIHNlbGVjdChnZW9fYWNjZXNzaW9uLCAKICAgICAgICAgdGl0bGUsIAogICAgICAgICB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCAKICAgICAgICAgdG5iY19zdWJ0eXBlLAogICAgICAgICBzdWJtaXNzaW9uX2RhdGUsCiAgICAgICAgIGVyLAogICAgICAgICBoZXIyLAogICAgICAgICBwciwKICAgICAgICAgcmFjZSwKICAgICAgICAgc2V0LAogICAgICAgICBnZW5kZXIsIAogICAgICAgICBhZ2VfeWVhcnMpICU+JSAKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIC5mbnMgPSBmYWN0b3IpKSAlPiUgCiAgbXV0YXRlKHRuYmNfc3VidHlwZSA9IGlmX2Vsc2UoaXMubmEoYXMuY2hhcmFjdGVyKHRuYmNfc3VidHlwZSkpLCAiTm90IEFwcGxpY2FibGUiLCBhcy5jaGFyYWN0ZXIodG5iY19zdWJ0eXBlKSkpICU+JSAKICBtdXRhdGUodG5iY19zdWJ0eXBlID0gZmFjdG9yKHRuYmNfc3VidHlwZSkpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkKCgpyb3duYW1lcyhtZGF0YV9zdWJzZXQpIDwtIGFzLmNoYXJhY3RlcihtZGF0YV9zdWJzZXQkZ2VvX2FjY2Vzc2lvbikKCmhlYWQobWRhdGFfc3Vic2V0KQoKYGBgCgpgYGB7cn0KIyByYXdEYXRhIDwtIHJlYWQuY2VsZmlsZXMoY2VsRmlsZXMsIHBoZW5vRGF0YSA9IEFubm90YXRlZERhdGFGcmFtZShtZGF0YV9zdWJzZXQpKQpgYGAKCgpgYGB7cn0KIyBzYXZlUkRTKG9iamVjdCA9IHJhd0RhdGEsICJyYXdEYXRhLlJEUyIpCnJhd0RhdGEgPC0gcmVhZFJEUygicmF3RGF0YS5SRFMiKQpgYGAKCgogIExvb2tpbmcgYXQgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIHJhdyBleHByZXNzaW9uIG1hdHJpeC4KCmBgYHtyfQpleHBycyhyYXdEYXRhKSAlPiUgZGltKCkKYGBgCgojIFBsb3R0aW5nIHNvbWUgbWV0YWRhdGEgYXR0cmlidXRlcwoKTG9va2luZyBhdCB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgZm9yIHRyaXBsZSBuZWdhdGl2ZSBzdGF0dXMgYW5kIGZvciBzZXQuCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICBjb3VudCh0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCBzZXQpCmBgYAoKCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICBjb3VudCh0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSAlPiUgCiAgbXV0YXRlKHByb3BvcnRpb24gPSByb3VuZChuL3N1bShuKSwgMykpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh5ID0gImR1bW15X2dyb3VwIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBwcm9wb3J0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLCBsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKwogIGdlb21fdGV4dChhZXMoeSA9IDEsIAogICAgICAgICAgICAgICAgeCA9IHByb3BvcnRpb24sIAogICAgICAgICAgICAgICAgbGFiZWwgPSBwcm9wb3J0aW9uLCAKICAgICAgICAgICAgICAgIGdyb3VwID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cyksIAogICAgICAgICAgICBzaXplID0gNSwgCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfZmlsbF9ucGcobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIikgKwogIGxhYnMoeCA9ICJQcm9wb3J0aW9uIiwKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlByb3BvcnRpb25zIG9mIFROQkMgc3RhdHVzIHZhbHVlcyBmb3IgR1NFNzYyNzUiLCA2MCkpCgpnZ3NhdmUoZmlsZW5hbWUgPSAicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfdG5iY19wcm9wb3J0aW9uX2JhcnBsb3QucG5nIikKYGBgCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICBjb3VudCh0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCBwcikgJT4lIAogIG11dGF0ZShwcm9wb3J0aW9uID0gcm91bmQobi9zdW0obiksIDMpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeCA9IHByLAogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHByb3BvcnRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cykpICsKICAgIGdlb21fdGV4dChhZXMoeCA9IHByLCAKICAgICAgICAgICAgICAgIHkgPSBwcm9wb3J0aW9uLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBncm91cCA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpLCAKICAgICAgICAgICAgc2l6ZSA9IDMsIAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgCiAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikgKwogICAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLCBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArCiAgc2NhbGVfZmlsbF9ucGcobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIikgKwogIGxhYnMoeCA9ICJQcm9nZXN0ZXJvbmUgUmVjZXB0b3IgU3RhdHVzIiwKICAgICAgIHkgPSAiUHJvcG9ydGlvbiIsCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJQcm9wb3J0aW9ucyBvZiBwcm9nZXN0ZXJvbmUgcmVjZXB0b3Igc3RhdHVzIHZhbHVlcyBmb3IgR1NFNzYyNzUiLCA2MCkpCgpnZ3NhdmUoZmlsZW5hbWUgPSAicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfcHJfcHJvcG9ydGlvbl9iYXJwbG90LnBuZyIpCmBgYAoKCmBgYHtyfQptZGF0YV9zdWJzZXQgJT4lIAogIGNvdW50KHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMsIGVyKSAlPiUgCiAgbXV0YXRlKHByb3BvcnRpb24gPSByb3VuZChuL3N1bShuKSwgMykpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcHJvcG9ydGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSkgKwogICAgICBnZW9tX3RleHQoYWVzKHggPSBlciwgCiAgICAgICAgICAgICAgICB5ID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHByb3BvcnRpb24sIAogICAgICAgICAgICAgICAgZ3JvdXAgPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSwgCiAgICAgICAgICAgIHNpemUgPSAzLCAKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIAogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIpICsKICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSksCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogIHNjYWxlX2ZpbGxfbnBnKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIpICsKICBsYWJzKHggPSAiRXN0cm9nZW4gUmVjZXB0b3IgU3RhdHVzIiwKICAgICAgIHkgPSAiUHJvcG9ydGlvbiIsCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJQcm9wb3J0aW9ucyBvZiBlc3Ryb2dlbiByZWNlcHRvciBzdGF0dXMgdmFsdWVzIGZvciBHU0U3NjI3NSIsIDYwKSkKCmdnc2F2ZShmaWxlbmFtZSA9ICJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9HU0U3NjI3NV9lcl9wcm9wb3J0aW9uX2JhcnBsb3QucG5nIikKYGBgCgpgYGB7cn0KbWRhdGFfc3Vic2V0ICU+JSAKICBjb3VudCh0cmlwbGVfbmVnYXRpdmVfc3RhdHVzLCBoZXIyKSAlPiUgCiAgbXV0YXRlKHByb3BvcnRpb24gPSByb3VuZChuL3N1bShuKSwgMykpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gaGVyMiwKICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBwcm9wb3J0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpKSArCiAgICBnZW9tX3RleHQoYWVzKHggPSBoZXIyLCAKICAgICAgICAgICAgICAgIHkgPSBwcm9wb3J0aW9uLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcHJvcG9ydGlvbiwgCiAgICAgICAgICAgICAgICBncm91cCA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpLCAKICAgICAgICAgICAgc2l6ZSA9IDMsIAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgCiAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikgKwogICAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLCBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArCiAgc2NhbGVfZmlsbF9ucGcobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIikgKwogIGxhYnMoeCA9ICJIRVIyIEFtcGxpZmljYXRpb24gU3RhdHVzIiwKICAgICAgIHkgPSAiUHJvcG9ydGlvbiIsCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJQcm9wb3J0aW9ucyBvZiBIRVIyIGFtcGxpZmljYXRpb24gc3RhdHVzIHZhbHVlcyBmb3IgR1NFNzYyNzUiLCA2MCkpCgpnZ3NhdmUoZmlsZW5hbWUgPSAicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfaGVyMl9wcm9wb3J0aW9uX2JhcnBsb3QucG5nIikKYGBgCgoKCmBgYHtyfQpsaXN0KCJFUiIgPSBtZGF0YV9zdWJzZXQkZ2VvX2FjY2Vzc2lvblttZGF0YV9zdWJzZXQkZXIgPT0gIlBvc2l0aXZlIl0sCiAgICAgIlBSIiA9IG1kYXRhX3N1YnNldCRnZW9fYWNjZXNzaW9uW21kYXRhX3N1YnNldCRwciA9PSAiUG9zaXRpdmUiXSwKICAgICAiSEVSMiIgPSBtZGF0YV9zdWJzZXQkZ2VvX2FjY2Vzc2lvblttZGF0YV9zdWJzZXQkaGVyMiA9PSAiUG9zaXRpdmUiXSkgJT4lCiAgZnJvbUxpc3QoLikgJT4lIAogIHVwc2V0KC4sIGMoIkVSIiwgIlBSIiwgIkhFUjIiKSwgd2lkdGhfcmF0aW8gPSAwLjIpICsKICBnZ3RpdGxlKHN0cl93cmFwKCJVcHNldCBwbG90IGZvciBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIEVSLCBQUiwgYW5kIEhFUjIgc3RhdHVzIGluIHRoZSBub24tVE5CQyBzYW1wbGVzIGluIEdTRTc2Mjc1IiwgNjApKSArCiAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9HU0U3NjI3NV9ub25UTkJDX3Vwc2V0LnBuZyIpCmBgYAoKUmVvcmRlcmluZyB0aGUgY29sdW1ucyBpbiB0aGUgbWV0YWRhdGEuCgpgYGB7cn0KbWRhdGEgPC0gc2VsZWN0KG1kYXRhLCBnZW9fYWNjZXNzaW9uLCBldmVyeXRoaW5nKCkpCmhlYWQobWRhdGEpCmBgYAoKCiMgUGVyZm9ybWluZyBSTUEKCiMjIFVzaW5nIHJlZ3VsYXIgUk1BIG9uIGRhdGEgKHdpdGhvdXQgc2VwYXJhdGluZyBieSBjbGFzcykKCmBgYHtyfQojIHJlc18xIDwtIHJtYShyYXdEYXRhKQpgYGAKCgpgYGB7cn0KIyBzYXZlUkRTKG9iamVjdCA9IHJlc18xLCAicmVzXzEuUkRTIikKcmVzXzEgPC0gcmVhZFJEUygicmVzXzEuUkRTIikKYGBgCgoKYGBge3J9CmV4cHJzKHJlc18xKSAlPiUgCiAgZGltKCkKYGBgCgoKYGBge3J9CmV4cHJzKHJlc18xKVsxOjUsIDE6NV0KYGBgCgoKIyMgUGVyZm9ybWluZyBjbGFzcy1zcGVjaWZpYyBSTUEgYnkgcmVhZGluZyBpbiB0aGUgZXhwcmVzc2lvbiBzZXRzIHNlcGFyYXRlbHkKCgpHZXR0aW5nIGxpc3RzIG9mIHRoZSBUTkJDIHNhbXBsZXMgYW5kIHRoZSBub24tVE5CQyBzYW1wbGVzLgoKYGBge3J9CnRuYmNfc2FtcGxlcyA8LSBtZGF0YV9zdWJzZXQgJT4lIAogIGZpbHRlcih0cmlwbGVfbmVnYXRpdmVfc3RhdHVzID09ICJUTiIpICU+JSAKICBzZWxlY3QoZ2VvX2FjY2Vzc2lvbikgJT4lIAogIHVubGlzdCh1c2UubmFtZXMgPSBGKSAlPiUgCiAgYXMuY2hhcmFjdGVyKCkKCmhlYWQodG5iY19zYW1wbGVzKQoKbm9udG5iY19zYW1wbGVzIDwtIG1kYXRhX3N1YnNldCAlPiUgCiAgZmlsdGVyKHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMgPT0gIm5vdCBUTiIpICU+JSAKICBzZWxlY3QoZ2VvX2FjY2Vzc2lvbikgJT4lIAogIHVubGlzdCh1c2UubmFtZXMgPSBGKSAlPiUgCiAgYXMuY2hhcmFjdGVyKCkKCmhlYWQobm9udG5iY19zYW1wbGVzKQpgYGAKCgpDcmVhdGluZyBkaWZmZXJlbnQgbWV0YWRhdGEgdGFibGVzIGZvciBUTkJDIGFuZCBub25UTkJDLgoKYGBge3J9Cm1kYXRhX3N1YnNldF90bmJjIDwtIG1kYXRhX3N1YnNldFt0bmJjX3NhbXBsZXMsIF0KZGltKG1kYXRhX3N1YnNldF90bmJjKQptZGF0YV9zdWJzZXRfbm9udG5iYyA8LSBtZGF0YV9zdWJzZXRbbm9udG5iY19zYW1wbGVzLCBdCmRpbShtZGF0YV9zdWJzZXRfbm9udG5iYykKYGBgCgoKUmVhZGluZyBpbiB0aGUgVE5CQyBmaWxlcy4KCmBgYHtyfQojIHJhd0RhdGFfdG5iYyA8LSByZWFkLmNlbGZpbGVzKGZpbGVuYW1lcyA9IGNlbEZpbGVzW3RuYmNfc2FtcGxlc10sIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YSA9IEFubm90YXRlZERhdGFGcmFtZShtZGF0YV9zdWJzZXRfdG5iYykpCiMgCiMgcmF3RGF0YV90bmJjCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMocmF3RGF0YV90bmJjLCBmaWxlID0gInJhd0RhdGFfdG5iYy5SRFMiKQpyYXdEYXRhX3RuYmMgPC0gcmVhZFJEUyhmaWxlID0gInJhd0RhdGFfdG5iYy5SRFMiKQpgYGAKCmBgYHtyfQpyYXdEYXRhX3RuYmMKYGBgCgoKUmVhZGluZyBpbiB0aGUgbm9uVE5CQyBmaWxlcy4KCmBgYHtyfQojIHJhd0RhdGFfbm9udG5iYyA8LSByZWFkLmNlbGZpbGVzKGZpbGVuYW1lcyA9IGNlbEZpbGVzW25vbnRuYmNfc2FtcGxlc10sIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YSA9IEFubm90YXRlZERhdGFGcmFtZShtZGF0YV9zdWJzZXRfbm9udG5iYykpCiMgCiMgcmF3RGF0YV9ub250bmJjCmBgYAoKCgpgYGB7cn0KIyBzYXZlUkRTKHJhd0RhdGFfbm9udG5iYywgZmlsZSA9ICJyYXdEYXRhX25vbnRuYmMuUkRTIikKcmF3RGF0YV9ub250bmJjIDwtIHJlYWRSRFMoZmlsZSA9ICJyYXdEYXRhX25vbnRuYmMuUkRTIikKYGBgCgoKYGBge3J9CnJhd0RhdGFfbm9udG5iYwpgYGAKCgpQZXJmb3JtaW5nIFJNQSBvbiBUTkJDIGRhdGEuCgpgYGB7cn0KIyByZXNfdG5iYyA8LSBybWEocmF3RGF0YV90bmJjKQpgYGAKCgpgYGB7cn0KIyBzYXZlUkRTKHJlc190bmJjLCBmaWxlID0gInJlc190bmJjLlJEUyIpCnJlc190bmJjIDwtIHJlYWRSRFMoZmlsZSA9ICJyZXNfdG5iYy5SRFMiKQpgYGAKCgpQZXJmb3JtaW5nIFJNQSBvbiBub25UTkJDIGRhdGEuCgpgYGB7cn0KIyByZXNfbm9udG5iYyA8LSBybWEocmF3RGF0YV9ub250bmJjKQpgYGAKCgpgYGB7cn0KIyBzYXZlUkRTKHJlc19ub250bmJjLCBmaWxlID0gInJlc19ub250bmJjLlJEUyIpCnJlc19ub250bmJjIDwtIHJlYWRSRFMoZmlsZSA9ICJyZXNfbm9udG5iYy5SRFMiKQpgYGAKCgpDb21iaW5pbmcgdGhlIGV4cHJlc3Npb24gbWF0cmljZXMgb2YgVE5CQyBhbmQgbm9uVE5CQyBkYXRhIGFmdGVyIHNlcGFyYXRlIFJNQS4KCmBgYHtyfQpyZXNfam9pbnQgPC0gY2JpbmQoZXhwcnMocmVzX3RuYmMpLCBleHBycyhyZXNfbm9udG5iYykpCmBgYAoKCmBgYHtyfQpyZXNfam9pbnRbMTo1LCAxOjVdCmBgYAoKIyBTYXZpbmcgY2VydGFpbiBDU1YgZmlsZXMgZm9yIGV2ZXJ5b25lIGVsc2UgdG8gcmVmZXIgdG8KClNhdmluZyB0aGUgam9pbnQgZXhwcmVzc2lvbiBtYXRyaXggZnJvbSBjbGFzcy1zcGVjaWZpYyBRTi4KCmBgYHtyfQpyZXNfam9pbnQgJT4lIAogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJwcm9iZV9pZCIpICU+JSAKICB3cml0ZV9jc3YoImRhdGFmcmFtZV9maWxlcy9wb3N0X2NsYXNzUU5fZXhwcmVzc2lvbi5jc3YiKQpgYGAKClNhdmluZyBhIHN1YnNldCBvZiB0aGUgbWV0YWRhdGEgdGhhdCBJIHRoaW5rIGlzIHJlbGV2YW50LgoKYGBge3J9Cm1kYXRhX3N1YnNldCAlPiUgCiAgd3JpdGVfY3N2KCJkYXRhZnJhbWVfZmlsZXMvbWV0YWRhdGFfc3Vic2V0LmNzdiIpCmBgYAoKClNhdmluZyB0aGUgVE5CQyBhbmQgbm9uVE5CQyBtZXRhZGF0YSBzZXBhcmF0ZWx5LCBqdXN0IGluIGNhc2UuCgpgYGB7cn0KbWRhdGFfc3Vic2V0X3RuYmMgJT4lIAogIHdyaXRlX2NzdigiZGF0YWZyYW1lX2ZpbGVzL21ldGFkYXRhX3N1YnNldF90bmJjLmNzdiIpCmBgYAoKCmBgYHtyfQptZGF0YV9zdWJzZXRfbm9udG5iYyAlPiUgCiAgd3JpdGVfY3N2KCJkYXRhZnJhbWVfZmlsZXMvbWV0YWRhdGFfc3Vic2V0X25vbnRuYmMuY3N2IikKYGBgCgoKIyBHZXR0aW5nIHNhbXBsZS1zcGVjaWZpYyBib3hwbG90cwoKIyMgU2FtcGxlIHNwZWNpZmljIGJveHBsb3RzIGZvciByZWd1bGFyIFJNQSBRTgoKCmBgYHtyfQpyZXNfMV9kZl9sb25nIDwtIHJlc18xICU+JQogIGV4cHJzKCkgJT4lIAogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJwcm9iZUlEIikgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gYWxsX29mKGModG5iY19zYW1wbGVzLCBub250bmJjX3NhbXBsZXMpKSwgbmFtZXNfdG8gPSAic2FtcGxlX2lkIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJpbnRlbnNpdHkiKSAlPiUgCiAgbGVmdF9qb2luKC4sIG1kYXRhX3N1YnNldCwgYnkgPSBjKCJzYW1wbGVfaWQiID0gImdlb19hY2Nlc3Npb24iKSkKYGBgCgoKYGBge3J9CiMgc2F2ZVJEUyhvYmplY3QgPSByZXNfMV9kZl9sb25nLCAicmVzXzFfZGZfbG9uZy5SRFMiKQpyZXNfMV9kZl9sb25nIDwtIHJlYWRSRFMoInJlc18xX2RmX2xvbmcuUkRTIikKYGBgCgoKYGBge3J9CnAyIDwtIHJlc18xX2RmX2xvbmcgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihzYW1wbGVfaWQsIGFzLm51bWVyaWMoc2V0KSksIHkgPSBpbnRlbnNpdHksIGNvbG9yID0gc2V0KSwgb3V0bGllci5zaXplID0gMC4yKQpgYGAKCgpgYGB7cn0KcDIgPC0gcDIgKyBsYWJzKHggPSAic2FtcGxlcyIsIAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiU2FtcGxlLXdpc2UgbG9nMiBpbnRlbnNpdHkgYm94cGxvdHMgZm9yIGdsb2JhbCBxdWFudGlsZSBub3JtYWxpemF0aW9uIGZvciBHU0U3NjI3NSIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX25wZyhuYW1lID0gIlRyaXBsZSBOZWdhdGl2ZSBTdGF0dXMiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsIAogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpCnAyCiAgCmBgYAoKCmBgYHtyfQpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X3Bvc3RfcmVnUU5fYm94cGxvdHMucG5nIiwgCiAgICAgICBwMiwgCiAgICAgICB1bml0cyA9ICJjbSIsIAogICAgICAgd2lkdGggPSAzMCwgCiAgICAgICBoZWlnaHQgPSAxMCkKYGBgCgoKYGBge3J9CnJtKHJlc18xX2RmX2xvbmcpCmBgYAoKCiMjIFNhbXBsZSBzcGVjaWZpYyBib3hwbG90cyBmb3IgY2xhc3NRTgoKCmBgYHtyfQpyZXNfam9pbnRfZGZfbG9uZyA8LSByZXNfam9pbnQgJT4lIAogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJwcm9iZUlEIikgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gYWxsX29mKGModG5iY19zYW1wbGVzLCBub250bmJjX3NhbXBsZXMpKSwgbmFtZXNfdG8gPSAic2FtcGxlX2lkIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJpbnRlbnNpdHkiKQogIApgYGAKCgpgYGB7cn0KIyBzYXZlUkRTKG9iamVjdCA9IHJlc19qb2ludF9kZl9sb25nLCAicmVzX2pvaW50X2RmX2xvbmcuUkRTIikKcmVzX2pvaW50X2RmX2xvbmcgPC0gcmVhZFJEUygicmVzX2pvaW50X2RmX2xvbmcuUkRTIikKYGBgCgoKCmBgYHtyfQpwMSA8LSByZXNfam9pbnRfZGZfbG9uZyAlPiUgCiAgbGVmdF9qb2luKC4sIG1kYXRhX3N1YnNldCwgYnkgPSBjKCJzYW1wbGVfaWQiID0gImdlb19hY2Nlc3Npb24iKSkgJT4lIAogIG11dGF0ZShzYW1wbGVfaWQgPSBmYWN0b3Ioc2FtcGxlX2lkKSkgJT4lIAogIGdncGxvdCgpICsKICAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoc2FtcGxlX2lkLCBhcy5udW1lcmljKHNldCkpLCB5ID0gaW50ZW5zaXR5LCBjb2xvciA9IHNldCksIG91dGxpZXIuc2l6ZSA9IDAuMikKYGBgCgoKYGBge3J9CnAxIDwtIHAxICsgbGFicyh4ID0gInNhbXBsZXMiLCAKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlNhbXBsZS13aXNlIGxvZzIgaW50ZW5zaXR5IGJveHBsb3RzIGZvciBjbGFzcy1zcGVjaWZpYyBxdWFudGlsZSBub3JtYWxpemF0aW9uIGZvciBHU0U3NjI3NSIsIDYwKSkgKwpzY2FsZV9jb2xvcl9ucGcobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIikgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLCAKICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKQoKcDEgCmBgYAoKCgpgYGB7cn0KZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9HU0U3NjI3NV9wb3N0X2NsYXNzUU5fYm94cGxvdHMucG5nIiwgCiAgICAgICBwMSwgCiAgICAgICB1bml0cyA9ICJjbSIsIHdpZHRoID0gMjAsIGhlaWdodCA9IDEwKQpgYGAKCgpgYGB7cn0KcmVzX2pvaW50X2RmX2xvbmcgJT4lIAogIGxlZnRfam9pbiguLCBtZGF0YV9zdWJzZXQsIGJ5ID0gYygic2FtcGxlX2lkIiA9ICJnZW9fYWNjZXNzaW9uIikpICU+JSAKICBtdXRhdGUoc2FtcGxlX2lkID0gZmFjdG9yKHNhbXBsZV9pZCkpICU+JSAKICBnZ3Bsb3QoKSArCiAgIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKHNhbXBsZV9pZCwgYXMubnVtZXJpYyhzZXQpKSwgeSA9IGludGVuc2l0eSwgZmlsbCA9IHNldCksIG91dGxpZXIuc2l6ZSA9IDAuMikgKwpsYWJzKHggPSAic2FtcGxlcyIsIAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiU2FtcGxlLXdpc2UgbG9nMiBpbnRlbnNpdHkgYm94cGxvdHMgZm9yIGNsYXNzLXNwZWNpZmljIHF1YW50aWxlIG5vcm1hbGl6YXRpb24gZm9yIEdTRTc2Mjc1IiwgNjApKSArCnNjYWxlX2ZpbGxfbnBnKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCAKICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIikKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvR1NFNzYyNzVfcG9zdF9jbGFzc1FOX2JveHBsb3RzX2JhZC5wbmciLCB1bml0cyA9ICJjbSIsIHdpZHRoID0gMjAsIGhlaWdodCA9IDEwKQpgYGAKCgojIFBlcmZvcm1pbmcgUENBCgojIyBDdXN0b20gZnVuY3Rpb25zCgpGdW5jdGlvbiB0byBjcmVhdGUgYW4gYW5ub3RhdGVkIGRhdGEgZnJhbWUgYnkgY29tYmluaW5nIFBDIHNjb3JlcyBhcyB3ZWxsIGFzIG1ldGFkYXRhOiB1c2VmdWwgZm9yIGdncGxvdCB2aXN1YWxpemF0aW9uLgoKYGBge3J9CmdldF9wY2FfYW5ub3RfZGYgPC0gZnVuY3Rpb24ocGNhLm9iaiwgc2FtcGxlX2lkX2NvbCwgbWRhdGFfZGYpewogIGluZF9zY29yZXMgPC0gcGNhLm9iaiR4CiAgaW5kX3Njb3Jlc19yZW9yZGVyZWQgPC0gaW5kX3Njb3Jlc1ttYXRjaChyb3duYW1lcyhpbmRfc2NvcmVzKSwgbWRhdGFfZGZbW3NhbXBsZV9pZF9jb2xdXSksIF0gJT4lIAogICAgYXNfdGliYmxlKHJvd25hbWVzID0gc2FtcGxlX2lkX2NvbCkgJT4lIAogICAgbXV0YXRlKGZpbGVuYW1lID0gZmFjdG9yKCEhc3ltKHNhbXBsZV9pZF9jb2wpKSkKICBpbmRfc2NvcmVzX2Fubm90IDwtIGxlZnRfam9pbihpbmRfc2NvcmVzX3Jlb3JkZXJlZCwgeSA9IG1kYXRhX2RmLCBieSA9IHNhbXBsZV9pZF9jb2wpICU+JSAKICBzZWxlY3QoYWxsX29mKGNvbG5hbWVzKG1kYXRhX3N1YnNldCkpLCBjb250YWlucygiUEMiKSkKICByZXR1cm4oaW5kX3Njb3Jlc19hbm5vdCkKfQpgYGAKCgojIyBQZXJmb3JtaW5nIFBDQSBvbiByZWd1bGFyIFJNQSBkYXRhCgpgYGB7cn0KIyBwY2EucmVzXzEgPC0gcmVzXzEgJT4lIAojICAgZXhwcnMoKSAlPiUgCiMgICB0KCkgJT4lIAojICAgcHJjb21wKGNlbnRlciA9IFRSVUUsIHNjYWxlID0gVFJVRSkKYGBgCgoKYGBge3J9CiMgc2F2ZVJEUyhwY2EucmVzXzEsICJwY2FfcmVzMS5SRFMiKQpwY2EucmVzXzEgPC0gcmVhZFJEUygicGNhX3JlczEuUkRTIikKYGBgCgoKR2V0dGluZyB0aGUgYW5ub3RhdGVkIGRhdGEgZnJhbWUgZm9yIHRoZSBQQ0EuCgpgYGB7cn0KcGNhLnJlc18xLmFubm90X2RmIDwtIGdldF9wY2FfYW5ub3RfZGYocGNhLm9iaiA9IHBjYS5yZXNfMSwgc2FtcGxlX2lkX2NvbCA9ICJnZW9fYWNjZXNzaW9uIiwgbWRhdGFfZGY9IG1kYXRhX3N1YnNldCkKaGVhZChwY2EucmVzXzEuYW5ub3RfZGYpCmBgYAoKIyMjIFZpc3VhbGl6aW5nIFBDQSByZXN1bHRzCgpMb29raW5nIGF0IHRoZSB2YXJpYW5jZSBleHBsYWluZWQgYnkgdGhlIGZpcnN0IDEwIFBDcy4KCmBgYHtyfQpmdml6X2VpZyhwY2EucmVzXzEpICsKICBsYWJzKHggPSAiUHJpbmNpcGFsIENvbXBvbmVudCIsIAogICAgICAgdGl0bGUgPSBzdHJfd3JhcCgiU2NyZWUgcGxvdCBmb3IgdGhlIGZpcnN0IDEwIHByaW5jaXBhbCBjb21wb25lbnRzIGZvciBnbG9iYWwgUk1BLW5vcm1hbGl6ZWQgZGF0YSBmb3IgR1NFNzYyNzUiLCA2MCkpICsKICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksIAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvUENBX3dob2xlUU5fc2NyZWUucG5nIiwgYmcgPSAid2hpdGUiKQpgYGAKClN1cGVyaW1wb3NpbmcgdmFyaWFibGVzIGluIGRhdGEgdXBvbiBzYW1wbGUgUENBIHNjb3Jlcy4KVGhlIFBDQSBkb2VzIG5vdCBzZWVtIHRvIHNlcGFyYXRlIHRoZSBUTkJDIGFuZCBub25UTkJDIHNhbXBsZXMgdGhhdCB3ZWxsIHdoZW4gcmVndWxhciBSTUEgaXMgcGVyZm9ybWVkLgoKYGBge3J9CmdncGxvdChwY2EucmVzXzEuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG91ciA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpKSArCiAgICBnZ3RpdGxlKHN0cl93cmFwKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIGNvbG91cmVkIGJ5IHRyaXBsZSBuZWdhdGl2ZSBzdGF0dXMgZm9yIEdTRTc2Mjc1IGFmdGVyIGdsb2JhbCBxdWFudGlsZSBub3JtYWxpemF0aW9uIiwgNjApKSArCiAgc2NhbGVfY29sb3JfbnBnKG5hbWUgPSAiVHJpcGxlIE5lZ2F0aXZlIFN0YXR1cyIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0gNCkpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9ICkKCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV93aG9sZVFOX1ROQkNfc3RhdHVzLnBuZyIsIGJnID0gIndoaXRlIikKICAKYGBgCgoKClRoZSBzYW1wbGVzIGRvIG5vdCBzZWVtIHRvIHNlcGFyYXRlIHdlbGwgYnkgc2V0IGVpdGhlci4KCmBgYHtyfQpnZ3Bsb3QocGNhLnJlc18xLmFubm90X2RmKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvdXIgPSBzZXQpKSArCiAgZ2d0aXRsZShzdHJfd3JhcCgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBjb2xvdXJlZCBieSBzZXQgKGRpc2NvdmVyeSBvciB2YWxpZGF0aW9uKSBmb3IgR1NFNzYyNzUgYWZ0ZXIgZ2xvYmFsIHF1YW50aWxlIG5vcm1hbGl6YXRpb24iLCA2MCkpICsKICBzY2FsZV9jb2xvcl9hYWFzKG5hbWUgPSAiU2FtcGxlIFNldCIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0gNCkpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvUENBX3dob2xlUU5fc2V0LnBuZyIsIGJnID0gIndoaXRlIikKYGBgCgpUaGVyZSBkb2VzIG5vdCBzZWVtIHRvIGJlIHRvbyBzdHJvbmcgb2YgYSBiYXRjaCBlZmZlY3QgYWNjb3JkaW5nIHRvIHN1Ym1pc3Npb24gZGF0ZS4KCmBgYHtyfQpnZ3Bsb3QocGNhLnJlc18xLmFubm90X2RmKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvdXIgPSBzdWJtaXNzaW9uX2RhdGUpKSArCiAgICBnZ3RpdGxlKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIFxuY29sb3VyZWQgYnkgc3VibWlzc2lvbiBkYXRlIGZvciB3aG9sZSBRTiIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0gNCkpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvUENBX3dob2xlUU5fc2V0LnBuZyIpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QocGNhLnJlc18xLmFubm90X2RmKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvdXIgPSB0bmJjX3N1YnR5cGUpKSArCiAgICBnZ3RpdGxlKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIFxuY29sb3VyZWQgYnkgdG5iY19zdWJ0eXBlIGZvciB3aG9sZSBRTiIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0gNCkpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KHggPSAwLjUsIHVuaXRzID0gImNtIikpIApgYGAKCiMjIFBlcmZvcm1pbmcgUENBIG9uIHNlcGFyYXRlbHktcGVyZm9ybWVkIFJNQSBkYXRhCgpgYGB7cn0KcGNhLnJlc19qb2ludCA8LSByZXNfam9pbnQgJT4lIAogIHQoKSAlPiUgCiAgcHJjb21wKGNlbnRlciA9IFRSVUUsIHNjYWxlID0gVFJVRSkKYGBgCgoKYGBge3J9CiMgc2F2ZVJEUyhwY2EucmVzX2pvaW50LCAicGNhX3Jlc19qb2ludC5SRFMiKQpwY2EucmVzX2pvaW50IDwtIHJlYWRSRFMoInBjYV9yZXNfam9pbnQuUkRTIikKYGBgCgoKR2V0dGluZyB0aGUgYW5ub3RhdGVkIGRhdGEgZnJhbWUgZm9yIHRoZSBQQ0EuCgpgYGB7cn0KcGNhLnJlc19qb2ludC5hbm5vdF9kZiA8LSBnZXRfcGNhX2Fubm90X2RmKHBjYS5vYmogPSBwY2EucmVzX2pvaW50LCBzYW1wbGVfaWRfY29sID0gImdlb19hY2Nlc3Npb24iLCBtZGF0YV9kZj0gbWRhdGFfc3Vic2V0KQpoZWFkKHBjYS5yZXNfam9pbnQuYW5ub3RfZGYpCmBgYAoKIyMjIFZpc3VhbGl6aW5nIFBDQSByZXN1bHRzCgpMb29raW5nIGF0IHRoZSB2YXJpYW5jZSBleHBsYWluZWQgYnkgdGhlIGZpcnN0IDEwIFBDcy4KCmBgYHtyfQpmdml6X2VpZyhwY2EucmVzX2pvaW50KSArCiAgbGFicyh4ID0gIlByaW5jaXBhbCBDb21wb25lbnQiLCAKICAgICAgIHRpdGxlID0gc3RyX3dyYXAoIlNjcmVlIHBsb3QgZm9yIHRoZSBmaXJzdCAxMCBwcmluY2lwYWwgY29tcG9uZW50cyBmb3IgY2xhc3Mtc3BlY2lmaWMgUk1BLW5vcm1hbGl6ZWQgZGF0YSBmb3IgR1NFNzYyNzUiLCA2MCkpICsKICAgIHRoZW1lKHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksIAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvUENBX2NsYXNzUU5fc2NyZWUucG5nIikKYGBgCgpTdXBlcmltcG9zaW5nIHZhcmlhYmxlcyBpbiBkYXRhIHVwb24gc2FtcGxlIFBDQSBzY29yZXMuClRoZSBQQ0EgKipkb2VzKiogc2VwYXJhdGUgdGhlIFROQkMgYW5kIG5vblROQkMgc2FtcGxlcyB3ZWxsIHdoZW4gY2xhc3Mtc3BlY2lmaWMgUk1BIGlzIHBlcmZvcm1lZC4KCmBgYHtyfQpnZ3Bsb3QocGNhLnJlc19qb2ludC5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3VyID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cykpICsKICAgIGdndGl0bGUoc3RyX3dyYXAoIlNhbXBsZXMgaW4gZmlyc3QgdHdvIFBDcywgY29sb3VyZWQgYnkgdHJpcGxlIG5lZ2F0aXZlIHN0YXR1cyBmb3IgR1NFNzYyNzUgYWZ0ZXIgY2xhc3Mtc3BlY2lmaWMgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX25wZyhuYW1lID0gIlRyaXBsZSBOZWdhdGl2ZSBTdGF0dXMiKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9IDQpKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV9jbGFzc1FOX1ROQkNfc3RhdHVzLnBuZyIpCmBgYApUaGUgdmFsaWRhdGlvbiBub25UTkJDIHNhbXBsZXMgYXJlIHNlcGFyYXRlZCBmcm9tIHRoZSBkaXNjb3ZlcnkgVE5CQyBhbmQgdmFsaWRhdGlvbiBUTkJDIHNhbXBsZXMuCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfam9pbnQuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG91ciA9IHNldCkpICsKICBnZ3RpdGxlKHN0cl93cmFwKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIGNvbG91cmVkIGJ5IHNldCAoZGlzY292ZXJ5IG9yIHZhbGlkYXRpb24pIGZvciBHU0U3NjI3NSBhZnRlciBjbGFzcy1zcGVjaWZpYyBxdWFudGlsZSBub3JtYWxpemF0aW9uIiwgNjApKSArCiAgc2NhbGVfY29sb3JfYWFhcyhuYW1lID0gIlNhbXBsZSBTZXQiKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9IDQpKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSkKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvUENBX2NsYXNzUU5fVE5CQ19zZXQucG5nIikKYGBgCgpTdWJtaXNzaW9uIGRhdGUgaXMgcGVyZmVjdGx5IGNvbmZvdW5kZWQgd2l0aCBUTkJDIHN0YXR1cy4gTWF5IG9yIG1heSBub3QgYmUgYmF0Y2ggZWZmZWN0cy4KCmBgYHtyfQpnZ3Bsb3QocGNhLnJlc19qb2ludC5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3VyID0gc3VibWlzc2lvbl9kYXRlKSkgKwogICAgZ2d0aXRsZShzdHJfd3JhcCgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBjb2xvdXJlZCBieSBzdWJtaXNzaW9uIGRhdGUgZm9yIEdTRTc2Mjc1IGFmdGVyIGNsYXNzLXNwZWNpZmljIHF1YW50aWxlIG5vcm1hbGl6YXRpb24iLCA2MCkpICsKICBzY2FsZV9jb2xvcl9ucGcobmFtZSA9ICJTdWJtaXNzaW9uIERhdGUiKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9IDQpKSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA3KSwKICAgICAgICAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV9jbGFzc1FOX1ROQkNfZGF0ZS5wbmciKQpgYGAKCgoKYGBge3J9CmdncGxvdChwY2EucmVzX2pvaW50LmFubm90X2RmKSArIAogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvdXIgPSB0bmJjX3N1YnR5cGUpKSArCiAgICBnZ3RpdGxlKHN0cl93cmFwKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIGNvbG91cmVkIGJ5IHN1YnR5cGUgb2YgVE5CQyBmb3IgR1NFNzYyNzUgYWZ0ZXIgY2xhc3Mtc3BlY2lmaWMgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiIsIDYwKSkgKwogICBzY2FsZV9jb2xvcl9uZWptKG5hbWUgPSAiVE5CQyBTdWJ0eXBlIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSwgCiAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KHggPSAwLjMsIHVuaXRzID0gImNtIiksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoeCA9IDAuMywgdW5pdHMgPSAiY20iKSkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAxKSkpCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV9jbGFzc1FOX1ROQkNfc3VidHlwZS5wbmciKQpgYGAKCmBgYHtyfQpnZ3Bsb3QocGNhLnJlc19qb2ludC5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gZmFjdG9yKHByKSwgc2hhcGUgPSB0cmlwbGVfbmVnYXRpdmVfc3RhdHVzKSkgKwogICAgZ2d0aXRsZShzdHJfd3JhcCgiU2FtcGxlcyBpbiBmaXJzdCB0d28gUENzLCBjb2xvdXJlZCBieSBwcm9nZXN0ZXJvbmUgcmVjZXB0b3Igc3RhdHVzIGZvciBHU0U3NjI3NSBhZnRlciBjbGFzcy1zcGVjaWZpYyBxdWFudGlsZSBub3JtYWxpemF0aW9uIiwgNjApKSArCiAgc2NhbGVfY29sb3JfbnBnKG5hbWUgPSAiUHJvZ2VzdGVyb25lIFJlY2VwdG9yIFN0YXR1cyIpICsKICBzY2FsZV9zaGFwZV9tYW51YWwobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIiwgdmFsdWVzID0gYygiVE4iID0gMTcsICJub3QgVE4iID0gMSkpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0gNCkpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDcpLAogICAgICAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMC41KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSwgCiAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KHggPSAwLjMsIHVuaXRzID0gImNtIiksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoeCA9IDAuMywgdW5pdHMgPSAiY20iKSkgKwogICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDEpKSkKCgpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL1BDQV9jbGFzc1FOX3ByX0dTRTc2Mjc1LnBuZyIsIGJnPSAid2hpdGUiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QocGNhLnJlc19qb2ludC5hbm5vdF9kZikgKyAKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFBDMSwgeSA9IFBDMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gZmFjdG9yKGhlcjIpLCBzaGFwZSA9IHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpKSArCiAgICBnZ3RpdGxlKHN0cl93cmFwKCJTYW1wbGVzIGluIGZpcnN0IHR3byBQQ3MsIGNvbG91cmVkIGJ5IEhFUjIgYW1wbGlmaWNhdGlvbiBzdGF0dXMgZm9yIEdTRTc2Mjc1IGFmdGVyIGNsYXNzLXNwZWNpZmljIHF1YW50aWxlIG5vcm1hbGl6YXRpb24iLCA2MCkpICsKICBzY2FsZV9jb2xvcl9uZWptKG5hbWUgPSAiSEVSMiBBbXBsaWZpY2F0aW9uIFN0YXR1cyIpICsKICBzY2FsZV9zaGFwZV9tYW51YWwobmFtZSA9ICJUcmlwbGUgTmVnYXRpdmUgU3RhdHVzIiwgdmFsdWVzID0gYygiVE4iID0gMTcsICJub3QgVE4iID0gMSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDUpLCAKICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoeCA9IDAuMywgdW5pdHMgPSAiY20iKSwKICAgICAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCh4ID0gMC4zLCB1bml0cyA9ICJjbSIpKSArIAogICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMSkpKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9QQ0FfY2xhc3NRTl9oZXIyX0dTRTc2Mjc1LnBuZyIsIGJnID0gIndoaXRlIikKYGBgCgpgYGB7cn0KZ2dwbG90KHBjYS5yZXNfam9pbnQuYW5ub3RfZGYpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBQQzEsIHkgPSBQQzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IGZhY3RvcihlciksIHNoYXBlID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cykpICsKICAgIGdndGl0bGUoc3RyX3dyYXAoIlNhbXBsZXMgaW4gZmlyc3QgdHdvIFBDcywgY29sb3VyZWQgYnkgZXN0cm9nZW4gcmVjZXB0b3Igc3RhdHVzIGZvciBHU0U3NjI3NSBhZnRlciBjbGFzcy1zcGVjaWZpYyBxdWFudGlsZSBub3JtYWxpemF0aW9uIiwgNjApKSArCiAgc2NhbGVfY29sb3JfbnBnKG5hbWUgPSAiRXN0cm9nZW4gUmVjZXB0b3IgU3RhdHVzIikgKwogIHNjYWxlX3NoYXBlX21hbnVhbChuYW1lID0gIlRyaXBsZSBOZWdhdGl2ZSBTdGF0dXMiLCB2YWx1ZXMgPSBjKCJUTiIgPSAxNywgIm5vdCBUTiIgPSAxKSkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPSA0KSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gNyksCiAgICAgICAgICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjUpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDUpLCAKICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoeCA9IDAuMywgdW5pdHMgPSAiY20iKSwKICAgICAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCh4ID0gMC4zLCB1bml0cyA9ICJjbSIpKSArIAogICAgICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMSkpKQoKCmdnc2F2ZSgicGxvdHMvZXhwbG9yYXRpb25fcGxvdHMvUENBX2NsYXNzUU5fZXJfR1NFNzYyNzUucG5nIikKYGBgCgojIFBlcmZvcm1pbmcgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKCiMjIEdldHRpbmcgZGlzdGFuY2VzCgpgYGB7cn0KcGVyZm9ybV9taW5fbWF4IDwtIGZ1bmN0aW9uKHgpewogIG1tX3RyYW5zZm9ybWF0aW9uIDwtIHByZVByb2Nlc3MoeCwgbWV0aG9kID0gInJhbmdlIikKICByZXNjYWxlZCA8LSBwcmVkaWN0KG1tX3RyYW5zZm9ybWF0aW9uLCB4KQogIHJldHVybihyZXNjYWxlZCkKfQpgYGAKCgpHZXR0aW5nIGRpc3RhbmNlcyBhZnRlciBwZXJmb3JtaW5nIG1pbiBtYXggbm9ybWFsaXphdGlvbi4KCgpgYGB7cn0KIyByZXNfMV9kaXN0cyA8LSBleHBycyhyZXNfMSkgJT4lIAojICAgdCgpICU+JSAKIyAgIHBlcmZvcm1fbWluX21heCgpICU+JSAKIyAgIGRpc3QobWV0aG9kID0gImV1Y2xpZGVhbiIpCiAgCmBgYAoKCmBgYHtyfQojIHNhdmVSRFMocmVzXzFfZGlzdHMsICJyZXNfMV9kaXN0cy5SRFMiKQpyZXNfMV9kaXN0cyA8LSByZWFkUkRTKCJyZXNfMV9kaXN0cy5SRFMiKQpgYGAKCgoKYGBge3J9CiMgcmVzX2pvaW50X2Rpc3RzIDwtIHJlc19qb2ludCAlPiUgCiMgICAgIHQoKSAlPiUgCiMgICBwZXJmb3JtX21pbl9tYXgoKSAlPiUgCiMgICBkaXN0KG1ldGhvZCA9ICJldWNsaWRlYW4iKQogIApgYGAKCgpgYGB7cn0KIyBzYXZlUkRTKHJlc19qb2ludF9kaXN0cywgInJlc19qb2ludF9kaXN0cy5SRFMiKQpyZXNfam9pbnRfZGlzdHMgPC0gcmVhZFJEUygicmVzX2pvaW50X2Rpc3RzLlJEUyIpCmBgYAoKCgojIyBVc2luZyBkZW5kcm9ncmFtcwoKYGBge3J9CnJlc18xX2RlbmQgPC0gcmVzXzFfZGlzdHMgJT4lIAogIGhjbHVzdCgpICU+JSAKICBhcy5kZW5kcm9ncmFtKCkKYGBgCgoKYGBge3J9CnJlc19qb2ludF9kZW5kIDwtIHJlc19qb2ludF9kaXN0cyAlPiUKICBoY2x1c3QoKSAlPiUgCiAgYXMuZGVuZHJvZ3JhbSgpCmBgYAoKIApgYGB7cn0KbGlicmFyeShkZW5kZXh0ZW5kKQpgYGAKCgpgYGB7cn0KIyByZXNfMV9kZW5kICU+JSAKIyAgIGxhYmVscygpCmBgYAoKCmBgYHtyfQojIHJlc18xX2RlbmQgJT4lIAojICAgb3JkZXIuZGVuZHJvZ3JhbSgpCmBgYAoKYGBge3J9CiMgKHJlc18xICU+JSAKIyAgIGV4cHJzKCkgJT4lIAojICAgY29sbmFtZXMoKSlbMjhdCiAgCmBgYAoKCmBgYHtyfQpyZXNfMV9kZW5kX2xhYm9yZGVyIDwtIHJlc18xX2RlbmQgJT4lIAogIGxhYmVscygpCgpgYGAKCgpgYGB7cn0KbXljb2xvcnMgPC0gaWZlbHNlKG1kYXRhX3N1YnNldFtyZXNfMV9kZW5kX2xhYm9yZGVyLCBdJHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMgPT0gIlROIiwgImZvcmVzdGdyZWVuIiwgIm1hcm9vbiIpCmBgYAoKCgpgYGB7cn0KcGFyKG1hciA9IGMoMTAsMiwxLDEpKQpyZXNfMV9kZW5kICU+JSAKICBzZXQoImxhYmVsc19jZXgiLCAwLjEpICU+JSAKICBwbG90KCkKCmNvbG9yZWRfYmFycyhjb2xvcnMgPSBteWNvbG9ycywgZGVuZCA9IHJlc18xX2RlbmQsIHJvd0xhYmVscyA9ICJUTiBTdGF0dXMiLCBhZGQgPSBUUlVFKQpgYGAKCgpgYGB7cn0KcmVzX2pvaW50X2RlbmRfbGFib3JkZXIgPC0gcmVzX2pvaW50X2RlbmQgJT4lIAogIGxhYmVscygpCmBgYAoKCmBgYHtyfQpteWNvbG9ycyA8LSBpZmVsc2UobWRhdGFfc3Vic2V0W3Jlc19qb2ludF9kZW5kX2xhYm9yZGVyLCBdJHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMgPT0gIlROIiwgImZvcmVzdGdyZWVuIiwgIm1hcm9vbiIpCmBgYAoKCgpgYGB7cn0KcGFyKG1hciA9IGMoMTAsMiwxLDEpKQpyZXNfam9pbnRfZGVuZCAlPiUgCiAgc2V0KCJsYWJlbHNfY2V4IiwgMC4xKSAlPiUgCiAgcGxvdCgpCgpjb2xvcmVkX2JhcnMoY29sb3JzID0gbXljb2xvcnMsIGRlbmQgPSByZXNfam9pbnRfZGVuZCwgcm93TGFiZWxzID0gIlROIFN0YXR1cyIsIGFkZCA9IFRSVUUpCmBgYAoKCiMjIFVzaW5nIGhlYXRtYXBzCgpGdW5jdGlvbiB0byBwcm9jZXNzIGRpc3RhbmNlIG9iamVjdCBpbnRvIGEgZGlzdGFuY2UgbWF0cml4IGZvciBoZWF0bWFwIHZpc3VhbGl6YXRpb24uCgpgYGB7cn0KZ2V0X2Rpc3RtYXQgPC0gZnVuY3Rpb24oeCl7CiAgZGlzdG1hdCA8LSBhcy5tYXRyaXgoeCkKICBjb2xuYW1lcyhkaXN0bWF0KSA8LSBOVUxMCiAgZGlhZyhkaXN0bWF0KSA8LSBOQQogIHJldHVybihkaXN0bWF0KQp9CmBgYAoKCgpgYGB7cn0Kcm93X2Fubm90IDwtIG1kYXRhX3N1YnNldCAlPiUgCiAgbXV0YXRlKGVyID0gZmFjdG9yKGVyKSwgcHIgPSBmYWN0b3IocHIpLCBoZXIyID0gZmFjdG9yKGhlcjIpKSAlPiUgCiAgc2VsZWN0KHN1Ym1pc3Npb25fZGF0ZSwgdHJpcGxlX25lZ2F0aXZlX3N0YXR1cywgcHIsIGVyLCBoZXIyKSAlPiUgCiAgcmVuYW1lKGBUTiBzdGF0dXNgID0gdHJpcGxlX25lZ2F0aXZlX3N0YXR1cykKCmhlYWQocm93X2Fubm90KQpgYGAKCgpgYGB7cn0Kc2V0LnNlZWQoNSkKcm93X2NvbG91cnMgPC0gbGlzdCggIlROIHN0YXR1cyIgPSBjKCJkYXJrZ29sZGVucm9kNCIsICJkYXJrbWFnZW50YSIpLCAKICAgICAgICAgICAgICAgICAgICAgInN1Ym1pc3Npb25fZGF0ZSIgPSBwYWxfbmVqbSgpKDIpLAogICAgICAgICAgICAgICAgICAgICAicHIiID0gcGFsX2xhbmNldCgpKDkpWzE6Ml0sIAogICAgICAgICAgICAgICAgICAgICAiZXIiID0gcGFsX2xhbmNldCgpKDkpWzg6OV0sCiAgICAgICAgICAgICAgICAgICAgICJoZXIyIiA9IHBhbF9sYW5jZXQoKSg5KVs1OjddKQoKbmFtZXMocm93X2NvbG91cnNbWyJUTiBzdGF0dXMiXV0pIDwtIGFzLmNoYXJhY3Rlcih1bmlxdWUocm93X2Fubm90W1siVE4gc3RhdHVzIl1dKSkKbmFtZXMocm93X2NvbG91cnMkcHIpIDwtIGFzLmNoYXJhY3Rlcih1bmlxdWUocm93X2Fubm90JHByKSkKbmFtZXMocm93X2NvbG91cnMkZXIpIDwtIGFzLmNoYXJhY3Rlcih1bmlxdWUocm93X2Fubm90JGVyKSkKbmFtZXMocm93X2NvbG91cnMkaGVyMikgPC0gYXMuY2hhcmFjdGVyKHVuaXF1ZShyb3dfYW5ub3QkaGVyMikpCm5hbWVzKHJvd19jb2xvdXJzJHN1Ym1pc3Npb25fZGF0ZSkgPC0gYXMuY2hhcmFjdGVyKHVuaXF1ZShyb3dfYW5ub3Qkc3VibWlzc2lvbl9kYXRlKSkKc3RyKHJvd19jb2xvdXJzKQpgYGAKYGBge3J9Cm15X2NvbG91cnMgPC0gIHZpcmlkaXMoMjY1XjIsIGJlZ2luID0gMSwgZW5kID0gMCkKYGBgCgpgYGB7cn0KcmVzX2pvaW50X2Rpc3RzICU+JSAKICBnZXRfZGlzdG1hdCgpICU+JSAKcGhlYXRtYXAoLiwKICAgICAgICAgY29sb3IgPSBteV9jb2xvdXJzLAogICAgICAgICBhbm5vdGF0aW9uX3JvdyA9IHJvd19hbm5vdCwKICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSByb3dfY29sb3VycywKICAgICAgICAgc2hvd19jb2xuYW1lcyA9IEYsCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGLAogICAgICAgICBjdXRyZWVfcm93cyA9IDIsCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMiwKICAgICAgICAgbWFpbiA9IHN0cl93cmFwKCJIZWF0bWFwIG9mIHNhbXBsZSBkaXN0YW5jZXMgZm9yIGNsYXNzLXNwZWNpZmljIFFOIGV4cHJlc3Npb24gbWF0cml4IGZvciBHU0U3NjI3NSIsIDYwKSwKICAgICAgICAgbGVnZW5kX2xhYmVscyA9IGMoInNtYWxsIGRpc3RhbmNlIiwgImxhcmdlIGRpc3RhbmNlIiksCiAgICAgICAgIGxlZ2VuZF9icmVha3MgPSBjKG1pbiguLCBuYS5ybSA9IFRSVUUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG1heCguLCBuYS5ybSA9IFRSVUUpKSwgCiAgICAgICAgICBmaWxlbmFtZSA9ICJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9jbGFzc1FOX2NsdXN0ZXJpbmdfaGVhdG1hcF9HU0U3NjI3NS5wbmciKQpgYGAKCgojIFBlcmZvcm1pbmcgU1ZBCgojIyBQZXJmb3JtaW5nIFNWQSBvbiByZWd1bGFyIFJNQSBkYXRhCgoKYGBge3J9CmZ1bGxfbW9kIDwtIG1kYXRhX3N1YnNldCAlPiUgCiAgc2VsZWN0KGdlb19hY2Nlc3Npb24sIHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpICU+JSAKICBhcnJhbmdlKHRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMpICU+JSAKICBtb2RlbC5tYXRyaXgofnRyaXBsZV9uZWdhdGl2ZV9zdGF0dXMsIGRhdGEgPSAuKQoKaGVhZChmdWxsX21vZCkKYGBgCgoKYGBge3J9CnJlZF9tb2QgPC0gbW9kZWwubWF0cml4KH4xLCBkYXRhID0gbWRhdGFfc3Vic2V0KQoKaGVhZChyZWRfbW9kKQpgYGAKCkdldCBudW1iZXIgb2Ygc2lnbmlmaWNhbnQgc3Vycm9nYXRlIHZhcmlhYmxlcy4KCmBgYHtyfQpuLnN2Lndob2xlUU4gPC0gbnVtLnN2KGV4cHJzKHJlc18xKSwgZnVsbF9tb2QsIG1ldGhvZD0ibGVlayIpCmBgYAoKCmBgYHtyfQpuLnN2Lndob2xlUU4KYGBgCgoKYGBge3J9CnN2b2JqLndob2xlUU4gPC0gc3ZhKGV4cHJzKHJlc18xKSwgbW9kID0gZnVsbF9tb2QsIG1vZDAgPSByZWRfbW9kLCBuLnN2ID0gMSkKYGBgCgoKYGBge3J9CnN2X2RmLndob2xlUU4gPC0gdGliYmxlKCJnZW9fYWNjZXNzaW9uIiA9IGNvbG5hbWVzKGV4cHJzKHJlc18xKSksICJzdiIgPSBzdm9iai53aG9sZVFOJHN2KQoKaGVhZChzdl9kZi53aG9sZVFOKQpgYGAKCgoKYGBge3J9CmxlZnRfam9pbihzdl9kZi53aG9sZVFOLCBtZGF0YSwgYnkgPSAiZ2VvX2FjY2Vzc2lvbiIpICU+JSAKICBtdXRhdGUoaW5kZXggPSA1KSAlPiUgCiAgZ2dwbG90KCkgKwogICMgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh5ID0gZmN0X3Jlb3JkZXIoZ2VvX2FjY2Vzc2lvbiwgc3YsIC5mdW4gPSBmdW5jdGlvbih4KXt4fSksIHggPSBzdiwgZmlsbCA9IHNldCkpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gc3VibWlzc2lvbl9kYXRlLCB5ID0gc3YsIGZpbGwgPSBzZXQpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgbGFicyh5ID0gIlN1cnJvZ2F0ZSBWYXJpYWJsZSBWYWx1ZSIsIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBsYXRlbnQgdmFyaWFibGUgZXN0aW1hdGVkIGJ5IFNWQSBmb3IgZGlmZmVyZW50IGdyb3VwaW5nIGZhY3RvcnMiKQogIAoKIyBnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL3N2YV9ncm91cGluZ19ub3JtYWxSTUEucG5nIikKYGBgCgoKIyMgUGVyZm9ybWluZyBTVkEgb24gY2xhc3Mtc3BlY2lmaWMgcXVhbnRpbGUgbm9ybWFsaXplZCBkYXRhCgpDcmVhdGUgZnVsbCBtb2RlbCBtYXRyaXguCgpgYGB7cn0KZnVsbF9tb2QgPC0gbWRhdGFfc3Vic2V0ICU+JSAKICBzZWxlY3QoZ2VvX2FjY2Vzc2lvbiwgdHJpcGxlX25lZ2F0aXZlX3N0YXR1cykgJT4lIAogIGFycmFuZ2UodHJpcGxlX25lZ2F0aXZlX3N0YXR1cykgJT4lIAogIG1vZGVsLm1hdHJpeCh+dHJpcGxlX25lZ2F0aXZlX3N0YXR1cywgZGF0YSA9IC4pCgpoZWFkKGZ1bGxfbW9kKQpgYGAKCkNyZWF0ZSByZWR1Y2VkIG1vZGVsIG1hdHJpeC4KCmBgYHtyfQpyZWRfbW9kIDwtIG1vZGVsLm1hdHJpeCh+MSwgZGF0YSA9IG1kYXRhX3N1YnNldCkKCmhlYWQocmVkX21vZCkKYGBgCgpHZXQgbnVtYmVyIG9mIHNpZ25pZmljYW50IHN1cnJvZ2F0ZSB2YXJpYWJsZXMuCgpgYGB7cn0Kbi5zdi5jbGFzc1FOIDwtIG51bS5zdihyZXNfam9pbnQsIGZ1bGxfbW9kLCBtZXRob2Q9ImxlZWsiKQpgYGAKCgpgYGB7cn0Kbi5zdi5jbGFzc1FOCmBgYAoKClBlcmZvcm0gU1ZBIG9uIGNsYXNzUU4tbm9ybWFsaXplZCBleHByZXNzaW9uIG1hdHJpeC4KCmBgYHtyfQpzdm9iai5jbGFzc1FOIDwtIHN2YShyZXNfam9pbnQsIG1vZCA9IGZ1bGxfbW9kLCBtb2QwID0gcmVkX21vZCwgbi5zdiA9IG4uc3YuY2xhc3NRTikKYGBgCgoKYGBge3J9CnN2X2RmLmNsYXNzUU4gPC0gdGliYmxlKCJnZW9fYWNjZXNzaW9uIiA9IGNvbG5hbWVzKHJlc19qb2ludCksICJzdiIgPSBzdm9iai5jbGFzc1FOJHN2KQoKaGVhZChzdl9kZi5jbGFzc1FOKQpgYGAKCmBgYHtyfQpzYXZlUkRTKHN2X2RmLmNsYXNzUU4sICJzdl9kZl9jbGFzc1FOLlJEUyIpCiMgc3ZfZGYuY2xhc3NRTiA8LSByZWFkUkRTKCJzdl9kZl9jbGFzc1FOLlJEUyIpCmBgYAoKCmBgYHtyfQpzdl9kZi5jbGFzc1FOIDwtIGxlZnRfam9pbihzdl9kZi5jbGFzc1FOICwgbWRhdGEsIGJ5ID0gImdlb19hY2Nlc3Npb24iKQpgYGAKCgpgYGB7cn0Kc3ZfZGYuY2xhc3NRTiAlPiUgCmdncGxvdCgpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gc3VibWlzc2lvbl9kYXRlLCB5ID0gc3YsIGZpbGwgPSBlcikpICsKICAjIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gc3VibWlzc2lvbl9kYXRlLCB5ID0gc3YsIGNvbG9yID0gZXIpKSArCiAgdGhlbWVfbGlnaHQoKSAKICAjIGxhYnMoeSA9ICJTdXJyb2dhdGUgVmFyaWFibGUgVmFsdWUiLCB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgbGF0ZW50IHZhcmlhYmxlIGVzdGltYXRlZCBieSBTVkEgZm9yIGRpZmZlcmVudCBncm91cGluZyBmYWN0b3JzIikKICAKCiMgZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9zdmFfZ3JvdXBpbmdfY2xhc3NRTi5wbmciKQpgYGAKCgojIFRyeWluZyB0byBzZWUgaWYgdGhlIFNWQSBlc3RpbWF0ZXMgYSBiYXRjaCB3aGVuIFFOIGlzIG5vdCBhcHBsaWVkCgpJbiB0aGlzIGF0dGVtcHQsIEkgcGVyZm9ybSBubyBxdWFudGlsZSBub3JtYWxpemF0aW9uIHdoaWxlIHBlcmZvcm1pbmcgUk1BLiBJZiBRTiBoYXMgbm90IGJlZW4gcGVyZm9ybWVkIGFuZCBhIHN1cnJvZ2F0ZSB2YXJpYWJsZSBzaG93cyB1cCB0aGF0IGNvcnJlc3BvbmRzIHRvIGJhdGNoLCBiYXRjaCBlZmZlY3RzIGFyZSBwcm9iYWJseSBwcmVzZW50LgoKYGBge3J9CnJhd0RhdGEuc3VtbWFyeSA8LSBybWEocmF3RGF0YSwgYmFja2dyb3VuZCA9IFRSVUUsIG5vcm1hbGl6ZSA9IEZBTFNFKQpgYGAKCgpgYGB7cn0KcmF3RGF0YS5zdW1tYXJ5X2RmX2xvbmcgPC0gcmF3RGF0YS5zdW1tYXJ5ICU+JSAKICBleHBycygpICU+JSAKICBhc190aWJibGUocm93bmFtZXMgPSAicHJvYmVJRCIpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGFsbF9vZihjKHRuYmNfc2FtcGxlcywgbm9udG5iY19zYW1wbGVzKSksIG5hbWVzX3RvID0gInNhbXBsZV9pZCIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiaW50ZW5zaXR5IikgJT4lIAogIGxlZnRfam9pbiguLCBtZGF0YV9zdWJzZXQsIGJ5ID0gYygic2FtcGxlX2lkIiA9ICJnZW9fYWNjZXNzaW9uIikpCmBgYAoKCgpgYGB7cn0KcDMgPC0gcmF3RGF0YS5zdW1tYXJ5X2RmX2xvbmcgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihzYW1wbGVfaWQsIGFzLm51bWVyaWMoc2V0KSksIHkgPSBpbnRlbnNpdHksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gc2V0KSkgKwogIGxhYnMoeCA9ICJzYW1wbGVzIiwgCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJTYW1wbGUtd2lzZSBsb2cyIGludGVuc2l0eSBib3hwbG90cyBpbiB0aGUgYWJzZW5jZSBvZiBRTiIsIDYwKSkgKwogIHNjYWxlX2NvbG9yX25wZygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKCnAzCmBgYAoKCmBgYHtyfQpnZ3NhdmUoInBsb3RzL2V4cGxvcmF0aW9uX3Bsb3RzL0dTRTc2Mjc1X25vUU5fYm94cGxvdHMucG5nIiwgCiAgICAgICBwMywgCiAgICAgICB1bml0cyA9ICJjbSIsIHdpZHRoID0gMzAsIGhlaWdodCA9IDEwKQpgYGAKCkdldHRpbmcgdGhlIG51bWJlciBvZiBzdXJyb2dhdGUgdmFyaWFibGVzIGluIHRoZSBhYnNlbmNlIG9mIHF1YW50aWxlIG5vcm1hbGl6YXRpb24uCgpgYGB7cn0Kbi5zdi5ub25vcm0gPC0gbnVtLnN2KGV4cHJzKHJhd0RhdGEuc3VtbWFyeSksIGZ1bGxfbW9kLCBtZXRob2Q9ImxlZWsiKQpgYGAKClRoZXJlIGlzIG9uZSBzdXJyb2dhdGUgdmFyaWFibGUgcHJlc2VudCBpbiB0aGUgYWJzZW5jZSBvZiBRTi4KCmBgYHtyfQpuLnN2Lm5vbm9ybQpgYGAKCgpgYGB7cn0Kc3ZvYmoubm9ub3JtIDwtIHN2YShleHBycyhyYXdEYXRhLnN1bW1hcnkpLCBtb2QgPSBmdWxsX21vZCwgbW9kMCA9IHJlZF9tb2QsIG4uc3YgPSBuLnN2Lm5vbm9ybSkKYGBgCgoKYGBge3J9CnN2X2RmLm5vbm9ybSA8LSB0aWJibGUoImdlb19hY2Nlc3Npb24iID0gY29sbmFtZXMoZXhwcnMocmF3RGF0YS5zdW1tYXJ5KSksICJzdiIgPSBzdm9iai5ub25vcm0kc3YpCgpoZWFkKHN2X2RmLm5vbm9ybSkKYGBgCgoKYGBge3J9CmxlZnRfam9pbihzdl9kZi5ub25vcm0sIG1kYXRhLCBieSA9ICJnZW9fYWNjZXNzaW9uIikgJT4lIAogIG11dGF0ZShpbmRleCA9IDUpICU+JSAKICBnZ3Bsb3QoKSArCiAgIyBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHkgPSBmY3RfcmVvcmRlcihnZW9fYWNjZXNzaW9uLCBzdiwgLmZ1biA9IGZ1bmN0aW9uKHgpe3h9KSwgeCA9IHN2LCBmaWxsID0gc2V0KSkgKwogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBzdWJtaXNzaW9uX2RhdGUsIHkgPSBzdiwgZmlsbCA9IHNldCkpICsKICB0aGVtZV9saWdodCgpICsKICBsYWJzKHkgPSAiU3Vycm9nYXRlIFZhcmlhYmxlIFZhbHVlIiwgCiAgICAgICB0aXRsZSA9IHN0cl93cmFwKCJEaXN0cmlidXRpb24gb2YgbGF0ZW50IHZhcmlhYmxlIGVzdGltYXRlZCBieSBTVkEgZm9yIGRpZmZlcmVudCBncm91cGluZyBmYWN0b3JzIiwgNjApKQoKZ2dzYXZlKCJwbG90cy9leHBsb3JhdGlvbl9wbG90cy9zdmFfZ3JvdXBpbmdfbm9RTi5wbmciKQpgYGAKCgpgYGB7cn0KCmBgYAoK